skip to content

How do you configure the MANIFEST.MF of a published Jar using the manifest {} block, and how can manifest attributes be shared across multiple jars?

level: middleimportance: should knowfreq 45%

answer

  1. manifest { attributes(mapOf(...)) }
  2. project.manifest then from(shared)
  3. eachEntry merge rules
  4. Automatic-Module-Name / Main-Class
  5. deterministic values for reproducibility

basics

~10 s

Inside a Jar task, use the manifest {} block and call attributes(mapOf(...)) to add entries like 'Implementation-Version' or 'Main-Class' to META-INF/MANIFEST.MF. To reuse, build a shared manifest and merge it.

solid answer

~40 s

Every `Jar` task exposes a `manifest {}` block backed by a `Manifest` object. You add entries with `attributes(mapOf("Implementation-Title" to "my-lib", "Implementation-Version" to project.version))`. Common entries for publishing include `Implementation-Title/Version/Vendor` and, for executable jars, `Main-Class`. To avoid duplicating attributes across several jars, create a shared manifest with `project.manifest { attributes(...) }` (or `java.manifest`) and merge it into each task via `from(sharedManifest)` inside the task's `manifest {}` block. You can also merge attributes from existing manifest files using `manifest.from("path/to/base.mf")` with optional `eachEntry` merge rules to resolve conflicts. Because attribute values are evaluated lazily by Gradle's task configuration, referencing `project.version` is safe even if set later. For reproducible publications, keep manifest values deterministic — avoid timestamps or build-host names that change between builds.

code

kotlin · 14 lines
kotlin
val sharedManifest = project.manifest {
    attributes(mapOf("Implementation-Vendor" to "Acme Corp"))
}

tasks.named<Jar>("jar") {
    manifest {
        from(sharedManifest)
        attributes(mapOf(
            "Implementation-Title" to project.name,
            "Implementation-Version" to project.version,
            "Automatic-Module-Name" to "com.acme.mylib"
        ))
    }
}

go deeper

for a junior

Show the manifest { attributes(mapOf(...)) } block and name a couple of standard attributes.

for a middle

Demonstrate sharing a manifest via project.manifest + from(), and mention Automatic-Module-Name / Main-Class use cases.

for a senior

Discuss eachEntry merge rules, keeping values deterministic for reproducibility, and which attributes downstream tooling reads.

for a principal

Standardize manifest identity attributes across modules in a convention plugin; govern which attributes are mandatory for published libraries.

## The manifest A Java archive carries a `META-INF/MANIFEST.MF` file: key/value attributes describing the jar. Gradle's `Jar` task models this through a `manifest {}` configuration block backed by a `Manifest` interface. ## Adding attributes ```kotlin tasks.named<Jar>("jar") { manifest { attributes(mapOf( "Implementation-Title" to project.name, "Implementation-Version" to project.version, "Implementation-Vendor" to "Acme Corp", "Main-Class" to "com.acme.Main" )) } } ``` `attributes(...)` adds to the **main** manifest section. You can also add named sections with `attributes(map, "sectionName")`. ## Sharing a manifest across jars A library that publishes a main jar plus a sources/javadoc jar often wants the same identity attributes everywhere. Define the manifest once and merge it: ```kotlin val sharedManifest = project.manifest { attributes(mapOf("Implementation-Vendor" to "Acme Corp")) } tasks.named<Jar>("jar") { manifest { from(sharedManifest) attributes(mapOf("Implementation-Title" to project.name)) } } ``` `from(...)` merges another `Manifest` (or a manifest file path) into this one. When the same key appears in both, the merged value wins unless you customize merging. ## Merge rules Gradle lets you reconcile conflicts when merging file-based manifests: ```kotlin manifest { from("src/config/base.mf") { eachEntry { if (key == "Implementation-Version" && baseValue != null) value = baseValue } } } ``` `eachEntry` runs a `ManifestMergeDetails` callback exposing `key`, `value`, `baseValue`, and `mergeValue`, so you can deterministically pick a winner. ## Reproducibility caution Manifest content is part of the jar bytes, so it affects byte-for-byte reproducibility. Avoid attributes that vary run-to-run — build timestamps, hostnames, random IDs — if you want identical published artifacts. Pair this with the archive task's `isPreserveFileTimestamps = false` and `isReproducibleFileOrder = true` settings. ## Why it matters for publishing Consumers and tooling read manifest attributes (e.g. `Implementation-Version`, `Automatic-Module-Name` for the JPMS module name) directly from your published jar, so getting them right is part of producing a high-quality publication.

  • How would you set the JPMS automatic module name via the manifest?
    Add the `Automatic-Module-Name` attribute in the jar's manifest block; the JVM uses it as the module name when the jar is on the module path without a module-info.
  • What problem can manifest attributes cause for reproducible builds?
    Volatile values like build timestamps or hostnames change the jar bytes between builds, breaking byte-for-byte reproducibility.

saying these in an interview costs you the question

  • Claiming you must hand-write META-INF/MANIFEST.MF and add it as a resource.
  • Saying manifest values can't reference project.version because of evaluation order (they're lazy).
  • Forgetting that manifest content affects reproducible-build byte equality.

context