skip to content

Where can artifact transforms be registered, and how do they become available to a project or an entire build?

level: middleimportance: should knowfreq 22%

answer

  1. registerTransform on DependencyHandler
  2. dependencies { registerTransform(...) }
  3. project-scoped, not global
  4. deliver via plugin / convention plugin
  5. shared Attribute defs (name+type equality)

basics

~20 s

You register transforms inside a project's dependencies { registerTransform(...) } block, usually from a plugin's apply. The registration is scoped to that project; to share, apply the plugin (or a convention/settings plugin) to every project that needs it.

solid answer

~40 s

`registerTransform` lives on the `DependencyHandler`, so it is registered per project via `dependencies { registerTransform(MyTransform::class) { ... } }`. The most common pattern is to do this from within a plugin's `apply(project)` so any project applying the plugin gets the transform. Registration is **project-scoped** — a transform registered in project A is not visible to project B unless B also registers it. To make a transform available build-wide, apply the registering plugin to every subproject (e.g. via a convention plugin in `buildSrc`/an included build, or by iterating `allprojects`/`subprojects`). Because registration is declarative and cheap, applying it everywhere is fine; the transform still only runs when something actually requests its `to` attributes. The transform's `Attribute` instances must be consistent (same name and type) across projects so matching lines up.

code

kotlin · 10 lines
kotlin
// convention plugin applied to every module makes the transform build-wide
class TransformsConventionPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        val artifactType = Attribute.of("artifactType", String::class.java)
        project.dependencies.registerTransform(Unzip::class.java) {
            it.from.attribute(artifactType, "jar")
            it.to.attribute(artifactType, "classes")
        }
    }
}

go deeper

for a junior

Know it goes in dependencies { registerTransform(...) }, typically from a plugin.

for a middle

Explain project-scoping and that plugins/convention plugins deliver the registration to many modules.

for a senior

Discuss settings/convention-plugin strategies and the need for shared, equal Attribute definitions across modules.

for a principal

Own a build-wide attribute schema and decide where transform registration lives so it's consistent and maintainable across the org.

## The registration surface `registerTransform` is a method on Gradle's `DependencyHandler`, which is why you call it inside the `dependencies { }` block: ```kotlin dependencies { registerTransform(Unzip::class) { from.attribute(artifactType, "jar") to.attribute(artifactType, "classes") } } ``` It can be called from a build script directly, but in real builds it almost always comes from a **plugin**: ```kotlin class UnzipPlugin : Plugin<Project> { override fun apply(project: Project) { project.dependencies.registerTransform(Unzip::class.java) { spec -> spec.from.attribute(artifactType, "jar") spec.to.attribute(artifactType, "classes") } } } ``` ## Scope: per-project Transforms are registered **per project**. A transform registered in `:app` is not automatically visible in `:lib`. If multiple projects need the same transform, each must register it — which is exactly why plugins are the delivery mechanism: apply the plugin to each project and the registration travels with it. To cover a whole multi-project build, common approaches are: - A **convention plugin** (in `buildSrc` or an included build) applied to every module. - A **settings plugin** that applies the convention plugin to all projects. - Iterating `subprojects { apply(plugin = ...) }` in the root build (older style, less preferred than convention plugins). ## Shared attribute definitions For matching to work across projects, the `Attribute<*>` objects must be **equal** — same name and same type. Two `Attribute.of("artifactType", String::class.java)` calls are equal because attribute equality is name+type. Define custom attributes in one shared place (often the plugin) so producers and consumers agree. ## Cost Registration is declarative and lazy, so registering a transform in many projects is cheap — it adds a rule, nothing runs until a resolution requests the target attributes. There is no harm in a project registering a transform it never ends up triggering.

  • Is a transform registered in one subproject visible to its siblings?
    No. Registration is project-scoped. Each project that needs the transform must register it, which is why convention plugins applied to every module are the usual delivery mechanism.
  • Why must Attribute definitions be shared across projects?
    Attribute equality is name+type. Producers and consumers must use equal Attribute instances for matching to line up, so define custom attributes once in a shared location.

saying these in an interview costs you the question

  • Claiming registerTransform makes a transform globally visible across all projects automatically.
  • Defining the same custom attribute with different types in different modules (breaks matching).
  • Worrying about cost of registering in many projects — it's lazy and cheap.

context