Skip to content
Merged
6 changes: 6 additions & 0 deletions api/shadow.api
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
}

Expand Down
28 changes: 27 additions & 1 deletion docs/application-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions docs/changes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -138,6 +143,16 @@ public abstract class ShadowApplicationPlugin : Plugin<Project> {
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<Distribution>
get() = named(DISTRIBUTION_NAME)

@get:JvmSynthetic
public inline val TaskContainer.runShadow: TaskProvider<JavaExec>
get() = named(SHADOW_RUN_TASK_NAME, JavaExec::class.java)

@get:JvmSynthetic
public inline val TaskContainer.startShadowScripts: TaskProvider<CreateStartScripts>
Expand All @@ -146,5 +161,13 @@ public abstract class ShadowApplicationPlugin : Plugin<Project> {
@get:JvmSynthetic
public inline val TaskContainer.installShadowDist: TaskProvider<Sync>
get() = named(SHADOW_INSTALL_TASK_NAME, Sync::class.java)

@get:JvmSynthetic
public inline val TaskContainer.shadowDistTar: TaskProvider<Tar>
get() = named(SHADOW_DIST_TAR_TASK_NAME, Tar::class.java)

@get:JvmSynthetic
public inline val TaskContainer.shadowDistZip: TaskProvider<Zip>
get() = named(SHADOW_DIST_ZIP_TASK_NAME, Zip::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Named>().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) {
Expand All @@ -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)
}
Expand All @@ -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"))
}
}
}
Expand All @@ -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<String> get() = dependsOn.filterIsInstance<Named>().map(Named::getName)
}
}