When you apply both the Application plugin and Shadow, how does shadowJar become runnable, and how do you make the build produce a runnable artifact by default?
answer
- Main-Class manifest attribute
- application.mainClass auto-wired
- runShadow / shadowDistZip
- not in assemble by default
- Kotlin MainKt suffix
basics
~10 sWith the application plugin applied, Shadow reads application.mainClass and writes a Main-Class manifest entry into the shadowJar, so java -jar app-all.jar works. You can wire shadowJar into the build/assembly graph so it's produced automatically.
solid answer
~40 sA jar is only directly runnable if its manifest has a `Main-Class` entry. Shadow integrates with the **Application plugin**: when `application` is applied and `application.mainClass` (or legacy `mainClassName`) is set, `shadowJar` automatically injects `Main-Class` into the manifest, making `java -jar build/libs/<name>-all.jar` work. Shadow also adds `runShadow` (run from the fat jar) and `installShadowDist`/`shadowDistZip`/`shadowDistTar` tasks that wrap the fat jar in a distribution. By default `shadowJar` is **not** part of `assemble`/`build`; if you want it built on every `./gradlew build`, wire a dependency, e.g. `tasks.assemble { dependsOn(tasks.shadowJar) }` or have the publishing/distribution artifact reference it. You can also override the manifest manually with `manifest { attributes("Main-Class" to "...") }` on the `shadowJar` task when not using the Application plugin. Keep the entry point single-sourced via `application.mainClass` to avoid drift between `run`, `runShadow`, and the manifest.
code
kotlin · 7 linesplugins {
application
id("com.gradleup.shadow") version "8.3.0"
}
application { mainClass = "com.example.MainKt" }
tasks.named("assemble") { dependsOn(tasks.shadowJar) }go deeper
Know that applying the application plugin and setting mainClass makes shadowJar runnable.
Explain Main-Class manifest wiring, runShadow/shadowDist tasks, and that shadowJar isn't in build by default.
Discuss single-sourcing the main class, the Kotlin MainKt suffix, and the build-cost trade-off of wiring it into assemble.
Standardize entry-point configuration and artifact-production policy across services/CI.
## What makes a jar runnable `java -jar foo.jar` works only if `META-INF/MANIFEST.MF` contains a `Main-Class:` attribute naming a class with a `public static void main(String[])`. A plain `shadowJar` without that attribute builds fine but errors with `no main manifest attribute` when launched. ## Application-plugin integration The Gradle **Application plugin** models a runnable program: you declare ```kotlin plugins { application id("com.gradleup.shadow") version "8.3.0" } application { mainClass = "com.example.MainKt" } ``` When Shadow sees the Application plugin, it: 1. Reads `application.mainClass` and writes `Main-Class` into the shadowJar manifest automatically. 2. Registers **`runShadow`** — runs the program from the fat jar. 3. Registers **`shadowDistZip` / `shadowDistTar` / `installShadowDist`** — distribution archives whose `lib/` contains the single fat jar plus generated start scripts. So `./gradlew shadowJar && java -jar build/libs/app-all.jar` just works. ## Making it build by default `shadowJar` is **not** hooked into `assemble`/`build` out of the box (the plain `jar` is). To always produce the fat jar: ```kotlin tasks.named("assemble") { dependsOn(tasks.shadowJar) } ``` or make it an output artifact of a configuration / distribution. Be deliberate: adding it to `build` makes every CI build pay the merge cost. ## Without the Application plugin If you don't use `application`, set the manifest yourself: ```kotlin tasks.shadowJar { manifest { attributes["Main-Class"] = "com.example.MainKt" } } ``` ## Single source of truth Keep the main class defined once (`application.mainClass`) so `run`, `runShadow`, and the manifest agree. Hard-coding it in the manifest while also setting `application.mainClass` invites drift. ## Kotlin gotcha For a top-level `main` in `Main.kt`, the generated class is `MainKt` — set `mainClass = "com.example.MainKt"`, not `com.example.Main`. ## Relationship to sibling distribution tasks Shadow's `shadowDistZip`/`installShadowDist` reuse the Application/Distribution machinery but package the fat jar instead of the thin jar plus a `lib/` of dependencies — useful when you want both a single jar and OS start scripts.
- Why does `java -jar app-all.jar` sometimes fail with 'no main manifest attribute'?The shadowJar manifest lacks a `Main-Class` entry — either the Application plugin isn't applied / `application.mainClass` isn't set, or you didn't set it manually on the shadowJar task's manifest.
- Is shadowJar part of `./gradlew build` by default?No. Only the plain `jar` is wired into assemble/build. You must add `tasks.assemble { dependsOn(tasks.shadowJar) }` (or otherwise depend on it) to build the fat jar automatically.
- For a Kotlin top-level main in Main.kt, what mainClass do you set?`com.example.MainKt` — Kotlin compiles a file-level main into a synthetic `<FileName>Kt` class.
saying these in an interview costs you the question
- Assuming shadowJar is automatically built by `build`.
- Setting Kotlin mainClass without the `Kt` suffix for a top-level main.
- Hard-coding Main-Class in two places, risking drift with application.mainClass.