skip to content

File Tasks: Copy, Sync, Delete

The built-in Copy, Sync, and Delete task types with from/into, include/exclude, rename, and expand. Interviewers also ask when to register a Copy task rather than call project.copy {} inline, which is a classic incrementality trap.

on this pageshow

questions

5

How do you define a task in Gradle that copies files from one directory into another, and what are the core DSL elements you use?

level: juniorimportance: must knowfreq 65%

answer

  1. Copy task type
  2. from / into
  3. include/exclude/rename/expand
  4. incremental + UP-TO-DATE
  5. register over create

basics

~10 s

Register a task of type Copy and configure from (source) and into (destination). Gradle copies the matched files into the target directory when the task runs.

solid answer

~30 s

Use the built-in `Copy` task type. You register it with `tasks.register("name", Copy::class)` and configure two essentials: `from(...)` declares one or more source files/dirs, and `into(...)` declares the destination directory. Inside the configuration you can add `include`/`exclude` ANT-style patterns to filter, `rename` to transform file names, and `expand`/`filter` to substitute tokens. `Copy` is incremental and idempotent — re-running it without input changes is UP-TO-DATE. Prefer `tasks.register` over `tasks.create` so the task is configured lazily. Avoid calling `project.copy {}` from the configuration phase for build outputs; use a real `Copy` task so Gradle tracks inputs/outputs and caching.

code

kotlin · 7 lines
kotlin
tasks.register<Copy>("copyConfig") {
    from("src/main/config")
    into(layout.buildDirectory.dir("config"))
    include("**/*.properties")
    exclude("**/*-local.properties")
    rename("(.+)\\.properties", "$1.props")
}

go deeper

for a junior

Name the Copy type and the from/into pair; show a minimal register block.

for a middle

Add include/exclude/rename and explain incrementality (UP-TO-DATE) and register-vs-create.

for a senior

Discuss CopySpec composition, nested specs for fan-out, and when imperative copy {} is acceptable vs a tracked task.

for a principal

Frame copy tasks as cacheable, declared-IO units in a reproducible build; govern conventions across modules.

## The `Copy` task type Gradle ships a built-in task type called `Copy`. A *task type* is a class that already knows how to do a job; you just register an instance and configure it. `Copy` extends `AbstractCopyTask`, which gives it a fluent file-copy DSL via the `CopySpec` interface. ### The two mandatory pieces - **`from(...)`** — the source. Can be a directory, a file, a `FileTree`, a `Configuration`, or even another `CopySpec`. Multiple `from` calls accumulate. - **`into(...)`** — the single destination directory. ```kotlin tasks.register<Copy>("copyDocs") { from("src/docs") into(layout.buildDirectory.dir("docs")) } ``` ### Filtering and transforming Because `Copy` implements `CopySpec`, you get: - `include("**/*.txt")` / `exclude("**/*.tmp")` — ANT glob patterns. - `rename("(.+)\\.txt", "$1.md")` — regex-based renaming. - `expand(mapOf("version" to project.version))` — Groovy `SimpleTemplateEngine` token substitution (`${version}`), useful for filtering text resources. - `filter { line -> ... }` / `eachFile { fcd -> ... }` — line- or file-level hooks; `eachFile` receives a `FileCopyDetails` so you can change `fcd.path`, `fcd.mode`, or call `fcd.exclude()`. ### Why a task, not `project.copy {}` `Copy` is an **incremental** task: Gradle snapshots its declared inputs (`from`) and outputs (`into`). If nothing changed, the task is skipped as `UP-TO-DATE`, and it participates in the build cache. Calling the imperative `project.copy {}` API does the copy immediately and is *not* tracked — fine for a quick one-off inside a task action, wrong for declaring build outputs. ### Laziness Use `tasks.register<Copy>(...)` (configuration-avoidance API) rather than `tasks.create`; the configuration block runs only if the task is actually needed in the task graph.

  • What happens if you call `into` twice in a Copy task?
    The last `into` wins — a Copy task has a single root destination directory. To copy different sources to different subdirectories you nest child `CopySpec`s with `from(...) { into("subdir") }`.
  • Why is `tasks.register` preferred over `tasks.create` here?
    `register` is the configuration-avoidance API: the configuration block runs lazily only if the task ends up in the graph, saving configuration-time work for builds that don't run it.

saying these in an interview costs you the question

  • Saying `into` accepts multiple destinations — it's a single root dir.
  • Using `project.copy {}` to declare build outputs (untracked, breaks incrementality/caching).

context

open as a page

What is the difference between the `Sync` task and the `Copy` task in Gradle, and when would you reach for `Sync`?

level: middleimportance: must knowfreq 55%

basics

~10 s

Sync copies like Copy but also DELETES anything in the destination that isn't in the source, leaving the target as an exact mirror. Copy only adds/overwrites; it never removes stale files.

open as a page

How do you delete files or directories in a Gradle build, and how does the built-in `clean` task relate to this?

level: juniorimportance: should knowfreq 45%

basics

~10 s

Register a Delete task and list paths via delete(...). The base plugin's clean task is just a Delete that removes the build directory (layout.buildDirectory).

open as a page

Explain how `rename`, `expand`, `filter`, and `eachFile` work inside a Copy task's CopySpec, and when you'd use each.

level: middleimportance: should knowfreq 40%

basics

~10 s

rename changes file names (regex/closure), expand substitutes ${token} placeholders in text, filter transforms content line-by-line, and eachFile gives a per-file hook to tweak path/permissions or exclude files.

open as a page

When assembling a complex output directory (e.g. an install image with libs, configs, and scripts in different subfolders), how do you structure Copy/Sync tasks, and what laziness and correctness pitfalls should you watch for?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Use one Sync/Copy task with nested from(...) { into("subdir") } child CopySpecs to fan files into subfolders. Wire inputs via Providers/configurations so they stay lazy, and target a dedicated build dir so Sync's delete is safe.

open as a page