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<>();