Your first build
The quickstart got you a cached build in under two minutes. This page walks through the same example slowly, naming each piece you saw.
The config
Section titled “The config”workspace: name: hello-giantcache: dir: ~/.cache/giant
targets: - name: "demo" inputs: ["name.txt"] outputs: ["greeting.txt"] command: "echo \"hello, $(cat name.txt)\" > greeting.txt"Three sections:
workspacenames the workspace. Used in cache keys and as the default label in the renderer.cachepoints to the local cache directory.~/.cache/giantis the default; you can override per-workspace.targetsis the list of things Giant knows how to build. Each target has aname, a list ofinputs, a list ofoutputs, and acommand.
This single file is the workspace root, so its targets live in the root
package and are addressed as //:name - here, //:demo. That’s enough
for a small project. As the repo grows you split config across
packages (one giant.yaml per directory), and a
large tree’s package files are usually
generated rather than hand-written.
That’s it for the minimum config. Everything else - tasks, remote cache, tags - is optional.
Anatomy of a target
Section titled “Anatomy of a target”- name: "demo" inputs: ["name.txt"] outputs: ["greeting.txt"] command: "echo \"hello, $(cat name.txt)\" > greeting.txt"nameis the target’s local name, unique within its package. The engine identity is the label//<package>:<name>- the package is the directory of thisgiant.yaml, so a root-file target nameddemois//:demo. Selection matches against the full label (see the selection language).inputsare file globs, package-relative by default. A bare glob likesrc/**/*.goresolves under the package directory; prefix//to reach the workspace root (//Cargo.lock). Anything they match contributes to the cache key.outputsare files the command produces. Package-relative unless//-prefixed, and resolved against the target’scwd(which defaults to the package directory). Giant fingerprints them after the build and stores them in the content-addressed cache.commandis a shell command. Giant runs it viash -cin the target’scwd.
The first build (cache miss)
Section titled “The first build (cache miss)”$ giant build✓ BUILD //:demo 4ms OK 1 built · 0 cached · 0 failed in 4msWhat happened, in order:
- Config load. Scan the tree for
giant.yamlfiles, merge them into one graph (here just the root file), resolve package-relative paths, validate. - Graph build. One target, no dependencies.
- Cache key compute. SHA-256 over: the command, the cwd, the
content hash of
name.txt, the env vars listed underbuilt_in_env, and the dependency hashes (none here). - Cache lookup. Local cache miss - first run.
- Execute. Run
echo "hello, $(cat name.txt)" > greeting.txt. - Fingerprint outputs. Hash
greeting.txt, store its bytes in the content-addressed store under the hash. - Write AC entry. Save an action-cache JSON file mapping the cache key to the output hashes.
The second build (cache hit)
Section titled “The second build (cache hit)”$ giant build✓ CACHE //:demo 1ms OK 0 built · 1 cached · 0 failed in 1ms- Config load + graph build - same as before.
- Cache key compute. Same inputs → same hash → same key.
- Cache lookup. Hit. Read the AC entry, pull
greeting.txt’s bytes out of CAS, write them to disk. - Done. No command was run.
The whole second-build path is dominated by file I/O. On a warm cache the in-process work is sub-millisecond per target.
Editing an input
Section titled “Editing an input”$ echo galaxy > name.txt$ giant build✓ BUILD //:demo 3msname.txt’s content hash changed (its bytes are different).- New cache key.
- Lookup misses.
- Build re-runs.
Where to go now
Section titled “Where to go now”- Concepts: the cache key - what feeds the hash and what doesn’t.
- Concepts: targets and inputs - full schema.
- CLI reference - every subcommand.