skip to content

Using artifactView's componentFilter, how would you separate project (local) artifacts from external module artifacts, and when is that useful?

level: juniorimportance: nice to knowfreq 20%

answer

  1. componentFilter { ComponentIdentifier }
  2. ProjectComponentIdentifier vs ModuleComponentIdentifier
  3. filter by identity, not file name
  4. fat-jar / lib copy / license scan
  5. lazy artifactFiles result

basics

~10 s

Set componentFilter { id -> id is ProjectComponentIdentifier } to keep only local project artifacts, or ModuleComponentIdentifier for external ones. Useful for fat-jar/relocation logic that should treat your own code differently from third-party libs.

solid answer

~40 s

`artifactView { componentFilter { id: ComponentIdentifier -> ... } }` receives the owning component's identifier for each artifact and keeps only those passing the predicate. The two main identifier types are `ProjectComponentIdentifier` (an artifact from a project in the build, including included builds) and `ModuleComponentIdentifier` (an external module from a repository). So `componentFilter { it is ProjectComponentIdentifier }` yields only your own modules' outputs, and `it is ModuleComponentIdentifier` yields only third-party jars. This separation is handy when packaging: e.g. a shadow/fat-jar that bundles only external libs but leaves project classes unshaded, copying only external dependencies into a `lib/` folder, or running a license scan on third-party artifacts only. Because the filter operates on the graph's component identity (not file names), it's reliable across versions and classifiers.

code

kotlin · 8 lines
kotlin
tasks.register<Copy>("collectExternalLibs") {
    from(
        configurations.getByName("runtimeClasspath").incoming.artifactView {
            componentFilter { it is ModuleComponentIdentifier }
        }.artifacts.artifactFiles
    )
    into(layout.buildDirectory.dir("lib"))
}

go deeper

for a junior

Know componentFilter lets you keep only project or only external artifacts using the identifier type.

for a middle

Distinguish ProjectComponentIdentifier vs ModuleComponentIdentifier and give a packaging use case.

for a senior

Explain why identity-based filtering beats file-name matching and how it composes with attributes/lenient and lazy wiring.

for a principal

Standardize a reusable plugin convention for splitting local vs external artifacts across packaging and compliance pipelines.

## What componentFilter receives `componentFilter { spec: Spec<ComponentIdentifier> }` is invoked per candidate artifact with the **`ComponentIdentifier` of the owning component**. Returning `true` keeps the artifact; `false` drops it. Crucially this filters by *component identity*, decided from the resolved graph — far more robust than matching file names. ## The identifier hierarchy - **`ProjectComponentIdentifier`** — the component is a project in the current build (or an included/composite build). Has `getProjectPath()`. - **`ModuleComponentIdentifier`** — an external module from a repository. Has `getGroup()`, `getModule()`, `getVersion()`. - Both implement `ComponentIdentifier`. ## Separating local from external ```kotlin val external = configurations.getByName("runtimeClasspath") .incoming.artifactView { componentFilter { it is ModuleComponentIdentifier } }.artifacts.artifactFiles val local = configurations.getByName("runtimeClasspath") .incoming.artifactView { componentFilter { it is ProjectComponentIdentifier } }.artifacts.artifactFiles ``` You can also narrow further inside the predicate, e.g. keep only a vendor's modules: ```kotlin componentFilter { it is ModuleComponentIdentifier && it.group == "com.acme" } ``` ## When this is useful - **Packaging / fat-jars**: bundle/relocate only external libraries while leaving project classes alone. - **Copying runtime libs**: place only third-party jars into a distribution `lib/` directory. - **Compliance**: run an SBOM/license scan over external artifacts only. - **Build splitting**: feed local project outputs to one step and external deps to another. ## Why not filter by file name? File names vary by version, classifier, and packaging, and can collide. The component identifier comes straight from the resolved graph, so the filter is stable and unambiguous. Combine with `lenient = true` if you want best-effort collection, and remember the resulting `artifactFiles` is a lazy `FileCollection` you can wire into a task input.

  • Which ComponentIdentifier type represents an artifact coming from another project in the build?
    ProjectComponentIdentifier — it has a projectPath and covers projects in the current build, including those from included/composite builds.
  • Why filter by component identifier rather than by jar file name?
    File names vary with version, classifier, and packaging and can collide; the component identifier comes from the resolved graph, giving a stable, unambiguous filter.

saying these in an interview costs you the question

  • Filtering by matching jar file-name substrings instead of component identity.
  • Assuming componentFilter receives a File — it receives a ComponentIdentifier.

context