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?
answer
- Copy task type
- from / into
- include/exclude/rename/expand
- incremental + UP-TO-DATE
- register over create
basics
~10 sRegister 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 sUse 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 linestasks.register<Copy>("copyConfig") {
from("src/main/config")
into(layout.buildDirectory.dir("config"))
include("**/*.properties")
exclude("**/*-local.properties")
rename("(.+)\\.properties", "$1.props")
}go deeper
Name the Copy type and the from/into pair; show a minimal register block.
Add include/exclude/rename and explain incrementality (UP-TO-DATE) and register-vs-create.
Discuss CopySpec composition, nested specs for fan-out, and when imperative copy {} is acceptable vs a tracked task.
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).