# Build Tasks

> Build targets, options, the build cache and build state.

`elide build` derives tasks from the manifest's source sets and artifacts and runs them in dependency order. Tasks
with unchanged inputs are skipped or restored from the cache.

## Running the build

```bash
elide build                   # run the default task group
elide build compile-java-main # run one task by name
elide build compile           # run a whole task group
elide build clean             # the special clean target
elide build --inspect         # print the task graph and its options without running it
elide build --no-cache        # ignore cached results and build state; rebuild everything
elide build --offline         # no network: no dependency fetches, no shared cache
elide build --workers=4       # run at most four tasks at once
```

## Targets

A bare argument names a task or group; a leading colon (`:compile-java-main`) is optional. No target runs `default`.
A target that matches nothing fails the build.

| Group | Alias | Tasks |
| --- | --- | --- |
| `default` | — | What a plain `elide build` runs. |
| `dependencies` | `deps` | Dependency resolution and install. |
| `compilation` | `compile` | Source compilation. |
| `testing` | `test` | Tests; also run by `elide test`. |

Some targets take options, listed by `--inspect`:

```bash
elide build test --select=class:com.example.SomeTest
elide build run --args="a b c"
```

In a [workspace](https://elide.help/docs/elide-pkl-reference#workspaces), prefix the project: `core:compile-kotlin-main`, `core:compile`,
or `core` for its default tasks. Unqualified targets resolve in the current project, then the root. `--inspect`
groups tasks by project, and progress rows show it (`[core] Compiling Kotlin sources`).

## Options

Options are `--name` or `--name=value`, anywhere among the targets. Arguments after `--` go to the process a task
launches.

| Option | Effect |
| --- | --- |
| `--inspect`, `-i` | List tasks and their options without running. |
| `--no-cache` | Disable the cache and local build state; every task runs. |
| `--offline` | No dependency downloads and no shared cache. |
| `--workers=N` | At most `N` concurrent tasks (default: processor count). `=` is required. |
| `--insights=FILE` | Run an [insight script](https://elide.help/docs/api-insight) in tasks that run guest code; repeatable. |
| `-f NAME[=VALUE]` | Build flag, read as `build.flags`; see [Build Flags](https://elide.help/docs/toolchain/build-flags). |

Options are checked against what the tasks declare before anything runs:

```text
Unknown option '--worker', did you mean '--workers'?
Option '--workers' requires a value, as '--workers=<value>'
```

A value on a bare flag, or a repeated non-repeatable option, fails the same way. Global flags (`-p`, `-v`, `--debug`
and the rest) are exempt. Task options: [Dependencies](https://elide.help/docs/toolchain/dependencies#maven-options),
[Testing](https://elide.help/docs/toolchain/testing#options).

## Build cache

The **local** tier is always on: `~/.elide/cache/entries-v2`, one directory per key with a record and a tar of the
outputs. Restores are verified against the recorded fingerprint. Entries in an older format miss.

The **shared** tier is an HTTP cache, enabled only by environment variables:

| Variable | Meaning |
| --- | --- |
| `ELIDE_REMOTE_CACHE` | Endpoint URL. Unset: no shared tier. |
| `ELIDE_REMOTE_CACHE_TOKEN` | Bearer token; takes precedence over basic auth. |
| `ELIDE_REMOTE_CACHE_USERNAME`, `ELIDE_REMOTE_CACHE_PASSWORD` | Basic auth; user info in the URL also works. |
| `ELIDE_REMOTE_CACHE_PUSH` | Upload this build's outputs (default `true`). |
| `ELIDE_REMOTE_CACHE_PULL` | Download misses (default `true`). |
| `ELIDE_REMOTE_CACHE_POLICY` | `eager` (default) transfers every entry; `smart` transfers only when it saves time. |

- Local is checked first; remote hits are stored locally.
- Typical setup: CI pushes; developers set `ELIDE_REMOTE_CACHE_PUSH=false`.
- Unreachable, slow or unreadable remote entries are misses. After three consecutive transport failures the tier is
  off for the rest of the build. HTTP errors such as `404` are not transport failures; a rejected credential warns
  once.
- The build fails on misconfiguration: a non-`http`/`https` URL, a password without a username, a non-boolean
  `PUSH`/`PULL`, or credentials sent over plain `http` to a non-loopback host.
- The cache serves executable outputs: use `https`, require authentication for writes, and limit who can write.
- `--offline` disables the shared tier for one run; `--no-cache` disables caching.

### `.eliderc` and the `smart` policy

Under `smart`, an entry is downloaded only when that is cheaper than rebuilding, and uploaded only when the upload
costs less than the work it saves. Costs come from `.eliderc` (`java.util.Properties` syntax): `~/.eliderc`, then
`<project>/.eliderc` over it. Do not commit it; it describes the machine.

```properties
# ~/.eliderc
network.downlink = 200mbps
network.uplink = 20mbps
network.latency = 250ms
network.stability = good        # good | fair | poor
machine.compute = fast          # slow | normal | fast, or a multiplier such as 1.5

cache.remote.policy = smart     # smart | eager (default)
cache.remote.upload-budget = 2.0
cache.remote.min-build = 500ms  # work cheaper than this is never published
```

`Mb/s` and `mbps` are megabits; `MB/s` is megabytes. Unreadable values warn and use the default.
`ELIDE_REMOTE_CACHE_POLICY` overrides `cache.remote.policy`. Under `eager`, the file has no effect.

## Build state

| File | Role |
| --- | --- |
| `.dev/elide.lock.v2.bin` | Portable per-task fingerprints, shareable across machines. Not a dependency lockfile. |
| `.dev/elide.build.bin` | Machine-local per-task fingerprints. |
| `.dev/elide.filehash.bin` | File digests keyed on size, times, inode and mode, so unchanged files are not re-read. |

- Binary files; do not edit.
- There is no unified dependency lockfile; npm writes `aube-lock.yaml`.
- In a workspace, state is stored in the root's `.dev/` for every member; outputs stay in each project's `.dev/`.
- `--no-cache` ignores the local state file but still reads the shared one.
- `elide install` reuses resolution when the dependency list is unchanged; `--fresh` re-resolves.
- Each compiler's arguments are written beside its output as `<tool>.args`, one per line.

## Timing a build

Each task prints its duration. For more:

```bash
elide build -v                                                # everything, at debug
ELIDE_LOG=dev.elide.tooling.build.telemetry=debug elide build # only the timings
```

```
[debug] dev.elide.tooling.build.telemetry stage graph took 590us
[debug] dev.elide.tooling.build.telemetry op compile-kotlin-main · up-to-date (up-to-date) took 420us
[debug] dev.elide.tooling.build.telemetry task compile-kotlin-main (skipped: up-to-date) took 12.6ms
```

Regions are a **stage** (configuration, graph, execution, state save), a **task**, or an **operation** on a task
(`prepare`, `fingerprint`, `up-to-date`, `execute`, cache lookups and stores). Work inside `execute` is not split
out.

`ELIDE_BUILD_TELEMETRY` writes a local [Chrome trace](https://perfetto.dev/docs/visualization/large-traces) when
the build ends; nothing is uploaded:

```bash
ELIDE_BUILD_TELEMETRY=1 elide build             # .dev/telemetry/build-<stamp>.trace.json
ELIDE_BUILD_TELEMETRY=profile.json elide build  # a file you name
ELIDE_BUILD_TELEMETRY=/tmp/traces elide build   # a directory you name
```

Open it in [Perfetto](https://ui.perfetto.dev), `chrome://tracing` or [speedscope](https://speedscope.app), or:

```bash
jq -r '.traceEvents[] | select(.ph=="X") | "\(.dur/1000 | floor)ms \(.cat) \(.name)"' \
  .dev/telemetry/build-*.trace.json | sort -rn | head
```
