An extra artifact attached via publication.artifact(...) appears in the POM but a Gradle consumer resolving by attributes can't 'see' it. Why, and what are the implications?
answer
- POM = flat file list by classifier
- GMM = variants with attributes
- raw artifact -> no variant -> no attribute match
- consumer must name classifier explicitly
- selectable file -> model as variant
basics
~20 sRaw artifact(...) files are listed in the POM by classifier, so Maven consumers can request them, but they aren't described as a variant in Gradle Module Metadata. Gradle's attribute-based resolution only selects variants, so it ignores these loose files.
solid answer
~50 s`publication.artifact(...)` attaches a file to the coordinates and writes it into the **POM** (and the bare file list), but it does **not** create a *variant* with attributes in **Gradle Module Metadata** (`.module`). Gradle's dependency resolution is variant-aware: a consumer requests attributes (e.g. `usage=java-runtime`, an OS attribute, a capability) and Gradle matches a *variant* from the metadata. A loose classified artifact has no attributes, so attribute-based selection can't target it — a Gradle consumer must explicitly ask for the classifier via an artifact selector (`dependency(...) { artifact { classifier = "all" } }`). The implication: for files you want *automatically* selected by Gradle consumers (per-platform jars, optional features), you should model them as **feature variants / outgoing variants** (consumable configurations with attributes) rather than raw artifacts. Raw artifacts are fine for Maven-style optional downloads (`-all`, `-docs`) that consumers fetch by classifier on demand.
code
kotlin · 11 lines// Raw artifact: POM-only, consumer must request the classifier explicitly
publishing.publications.named<MavenPublication>("maven") {
artifact(tasks.named("fatJar")) { classifier = "all" }
}
// Consumer side — attribute resolution will NOT find -all; you must ask:
dependencies {
implementation("com.example:mylib:1.0") {
artifact { classifier = "all"; extension = "jar" }
}
}go deeper
Recognize that an extra artifact shows up in the POM and is requested by classifier.
Explain that POM lists files but Gradle Module Metadata describes variants, and raw artifacts aren't variants.
Articulate why attribute-based resolution ignores raw artifacts and when to switch to feature/outgoing variants.
Decide org-wide whether per-platform/optional content is modeled as classifier artifacts (Maven interop) or variants (Gradle-native selection), trading interoperability against rich resolution.
## Two publishing models living side by side When you publish, Gradle can write two metadata files: - **`pom.xml`** — the Maven view: coordinates, dependencies, and a flat list of files distinguished by classifier/type. Maven and older Gradle consumers read this. - **`*.module` (Gradle Module Metadata, GMM)** — Gradle's richer view: **variants**, each with **attributes** (usage, category, OS, etc.), **capabilities**, and the files belonging to that variant. ## What `artifact(...)` touches A raw `artifact(tasks.named("fatJar")) { classifier = "all" }`: - Adds the file to the published files and references it in the POM by classifier. - Does **not** add a new variant to GMM. There is no attribute set describing *when* a consumer should pick this file. ## Why Gradle consumers can't auto-select it Gradle resolution is **variant matching**: the consumer declares requested attributes on a resolvable configuration, and Gradle finds the producer **variant** whose attributes are compatible. A classified file with no attributes simply isn't a candidate in that matching — it's invisible to attribute-based selection. The consumer can still fetch it, but only by *explicitly* naming the classifier: ```kotlin dependencies { implementation("com.example:mylib:1.0") { artifact { classifier = "all"; extension = "jar" } } } ``` This bypasses variant resolution and pulls just that file — losing transitive metadata for that artifact. ## The design implication If the extra file represents a **selectable capability** — a Linux vs Windows native jar, a `-jdk8` vs `-jdk11` build, an optional feature with its own dependencies — model it as a **variant**: register a consumable configuration (`configurations.consumable(...)`), set attributes, attach the artifact to *that* configuration, and let it flow into GMM (e.g. via `java.registerFeature(...)` for feature variants, or a custom `AdhocComponentWithVariants`). Then Gradle consumers select it automatically by attributes/capabilities. If the file is just an **optional download** with no resolution semantics (a fat `-all` jar, a `-docs` zip for humans), a raw `artifact(...)` is the right, simpler tool — you accept that it's POM-only and fetched by classifier. ## Summary table | Need | Use | |------|-----| | Optional file fetched by classifier, Maven-friendly | raw `artifact(...)` | | File auto-selected by Gradle attributes/capabilities | variant (feature variant / outgoing variant) | This boundary is the crux: `artifact(...)` is about *attaching files*, not about *describing resolution*.
- How would you make the extra file auto-selectable by Gradle consumers?Model it as a variant: create a consumable configuration with attributes (or use `java.registerFeature` / an outgoing variant), attach the artifact there so it lands in Gradle Module Metadata. Then consumers match it by attributes/capabilities.
- What does a Gradle consumer lose when fetching a raw classified artifact?It bypasses variant resolution and gets just that file with no variant-level transitive dependencies or attribute matching — purely a Maven-style classifier fetch.
A raw artifact is like extra items stuffed in a parcel with a sticky note — you can ask for them by name, but the shipping label (the metadata Gradle reads to auto-route) doesn't mention them. A variant is printed on the label, so the sorting machine routes it automatically.
saying these in an interview costs you the question
- Asserting that raw artifacts appear as variants in Gradle Module Metadata — they don't.
- Telling a Gradle consumer they'll automatically resolve a classified artifact by usage attribute.
- Conflating 'attached file' (artifact) with 'selectable variant' (configuration + attributes).