skip to content

A teammate added `tasks.register<Jar>("sourcesJar")` and `publication.artifact(sourcesJar)` instead of `withSourcesJar()`, and now consumers can't resolve sources via attributes. Why, and how do you fix it properly?

level: seniorimportance: should knowfreq 25%

answer

  1. artifact() = file only, no variant
  2. module metadata lacks DocsType=sources
  3. attribute resolution can't match
  4. fix = withSourcesJar()
  5. or addVariantsFromConfiguration on AdhocComponentWithVariants

basics

~20 s

artifact(sourcesJar) only attaches a file to the Maven publication; it doesn't create a documentation variant on the java component, so Gradle Module Metadata has no sources variant to match. Use withSourcesJar() (or attach the variant via AdhocComponentWithVariants).

solid answer

~40 s

The manual approach publishes the file but skips component integration. `publication.artifact(sourcesJar)` adds a classified artifact to the Maven POM/publication, yet the `java` SoftwareComponent never learns about a sources variant — so the generated Gradle Module Metadata (`module.json`) contains no `DocsType=sources` variant. Attribute-aware consumers (IDEs requesting sources, `requestAttributes`) have nothing to match, so resolution by attribute fails even though the `-sources.jar` is physically in the repo. The proper fix is to delete the manual task/artifact wiring and call `java { withSourcesJar() }`, which registers the task AND attaches a `sourcesElements` consumable variant to the component. If they need bespoke contents, configure the helper-created `sourcesJar` task by name. Only as a last resort would you keep a custom task and manually attach it via `(components["java"] as AdhocComponentWithVariants).addVariantsFromConfiguration(sourcesElements) { }`.

code

kotlin · 10 lines
kotlin
// remove the manual artifact() wiring, then:
java { withSourcesJar() }

publishing {
    publications {
        named<MavenPublication>("maven") {
            from(components["java"]) // now publishes the sources VARIANT + metadata
        }
    }
}

go deeper

for a junior

Recognize that withSourcesJar() is the recommended way and the manual one misses something.

for a middle

Explain that artifact() publishes a file but not a component variant, so metadata lacks the sources variant.

for a senior

Distinguish the publication-artifact layer from the component-variant/module-metadata layer and give the helper or addVariantsFromConfiguration fix.

for a principal

Establish a publishing convention so teams never hand-roll classified jars and lose attribute-aware consumption org-wide.

## Two layers: file vs. variant Publishing has two distinct layers: 1. **Maven publication artifacts** — files with classifiers written to the POM/repo. `publication.artifact(sourcesJar)` operates here. 2. **Component variants / Gradle Module Metadata** — the richer, attribute-tagged description of what the module offers (`apiElements`, `runtimeElements`, documentation variants). Attribute-based resolution reads *this*. The manual approach touches only layer 1. The physical `-sources.jar` exists, but `module.json` has no variant with `org.gradle.docstype=sources`, so a consumer doing attribute-based selection (e.g. an IDE or a `dependencies { }` request for sources) can't resolve it. ## The correct fix Replace the manual wiring with the helper: ```kotlin java { withSourcesJar() } // registers sourcesJar + sourcesElements variant on the java component ``` Now `from(components["java"])` publishes the variant and writes it into module metadata, restoring attribute-based resolution. ## If you must keep a custom task When the helper's defaults truly don't fit, register your own consumable configuration and attach it to the component: ```kotlin val sourcesElements by configurations.consumable("sourcesElements") { attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES)) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) } outgoing.artifact(tasks.named<Jar>("customSourcesJar")) } (components["java"] as AdhocComponentWithVariants) .addVariantsFromConfiguration(sourcesElements) {} ``` This reproduces exactly what `withSourcesJar()` automates — which is the whole argument for just using the helper. ## Interview takeaway Publishing a classified jar is not the same as declaring a variant. The `withXxxJar()` methods exist precisely to bridge file-level publishing and variant-aware module metadata.

  • Is the `-sources.jar` file actually missing from the repository in the manual case?
    No — the file is published. What's missing is the variant entry in Gradle Module Metadata, so attribute-based resolution can't match it even though the file exists.
  • How would you attach a custom sources task to the java component without the helper?
    Create a consumable configuration with `Category=documentation`/`DocsType=sources` attributes, add the jar as its outgoing artifact, then cast the java component to `AdhocComponentWithVariants` and call `addVariantsFromConfiguration`.

saying these in an interview costs you the question

  • Claiming the file isn't published — it is; only the variant/metadata is missing.
  • Thinking `artifact(...)` and `withSourcesJar()` are equivalent.

context