Toolchain Setup

What you need, in the order you need it. Phases 0–2 of the browser track require no local Chromium build — that is deliberate, and it is what keeps the track moving while a 100 GB checkout and a toolchain upgrade happen in the background.


Tier 0 — needed on day one (zero cost)

ToolWhy
Chrome / Chromium (stock)every observation lab; DevTools; --enable-blink-features
Chromium Code Searchsource.chromium.org/chromium/chromium/srcsource navigation with cross-references and blame, no checkout
Perfetto UIui.perfetto.devfull traces across processes and threads
Node.jsthe framework-track labs and their spec harnesses
The specs — WHATWG HTML/DOM, CSS, Web IDLthe contract the implementation is written against

That is enough for bi-01, bi-03, and all of fw-*.

DevTools settings worth changing once

  • CPU throttling 4×/6× — unthrottled desktop results are not evidence about your users.
  • Rendering panel: paint flashing, layer borders, scroll-performance issues, frame rendering stats.
  • Performance panel: enable "Screenshots" and "Memory".
  • Experiments: enable the timeline's advanced rendering instrumentation if offered.

Tier 1 — the checkout (large, but useful before it compiles)

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git ~/depot_tools
export PATH="$PATH:$HOME/depot_tools"          # add to ~/.zshrc
mkdir ~/chromium && cd ~/chromium
caffeinate fetch --git-cache chromium

Budget the disk honestly. Measured on the reference machine (2026-08-10):

ItemSize
~/chromium/src26 GB
git cache mirror24 GB
one component build, modest symbols15–25 GB

The mirror location comes from cache_dir in ~/chromium/.gclient — it is not ~/.cache, and confusing the two sends you deleting the wrong thing. fetch --git-cache roughly doubles peak storage; the mirror is a cache and can be deleted once src is synced.

What the checkout gives you before it ever compiles

This is the part people miss. Without a working compiler you still get:

git grep -n 'CausesFosterParenting' -- third_party/blink/renderer/core/html/parser/
git log -S'mac_sdk_official_version = "26.5"' -- build/config/mac/mac_sdk.gni
git log --follow --oneline -- <file>          # survives the ng_* renames

git grep over 30M lines returns in well under a second — faster than Code Search — and git log -S is the archaeology tool the source-reading ladder depends on. Plus every in-tree document, every .json5, .idl, and .mojom.


Tier 2 — the build (needed from Phase 3)

cd ~/chromium/src
gn gen out/Default

out/Default/args.gn:

is_debug = false                    # release codegen: much faster builds
is_component_build = true           # small dylibs, fast incremental links — essential
symbol_level = 1                    # function names + lines
blink_symbol_level = 2              # full symbols where you set breakpoints
dcheck_always_on = true             # keep assertions: the highest-value learning flag
autoninja -C out/Default content_shell    # prefer this over `chrome`
gn ls out/Default | grep -E ':(content_shell|blink_tests)$'   # never guess target names

dcheck_always_on = true is the flag most people omit and the one that matters most for learning: DCHECKs are Blink's invariants written as executable assertions, so breaking one produces a message naming the invariant instead of a confusing misrender.

Known blocker on the reference machine

Chromium trunk requires macOS 26.2+ / Xcode 26.5+ / SDK 26.5 (the requirement landed 2026-05-13). On macOS 15.0 / Xcode 16.2 the build fails on missing SDK symbols such as posix_spawn_file_actions_addchdir, and no gn flag can fix it — __builtin_available is a runtime check but the symbol must exist at compile time.

Full diagnosis, the two gn workarounds that get gn gen through, and the ranked options are in the build guide §2.0.


Tier 3 — debugging and tests

echo "command script import ~/chromium/src/tools/lldb/lldbinit.py" >> ~/.lldbinit

Attach to a renderer, not the browser process:

out/Default/Content\ Shell.app/Contents/MacOS/Content\ Shell \
  --renderer-startup-dialog --disable-hang-monitor <url>
lldb -p <pid printed by the dialog>

--disable-hang-monitor matters: without it, sitting at a breakpoint for 30 seconds gets your renderer killed and you lose the state you were inspecting.

Tests:

autoninja -C out/Default blink_tests
third_party/blink/tools/run_web_tests.py -t Default fast/forms
out/Default/content_shell --run-web-tests <path>

Known failures live in third_party/blink/web_tests/TestExpectations (9,418 lines) — which is also your candidate pool for a first contribution.


Building this book

bash build.sh          # installs a pinned mdBook if absent, writes ./book and ./dist/book
mdbook serve           # live reload at http://localhost:3000
python3 tools/gen-summary.py   # regenerate SUMMARY.md after adding modules or steps

SUMMARY.md is generated, not hand-maintained: a module has up to 13 pages across ~35 modules, and a mistyped link silently drops a page from the book.


A standing rule

Every Chromium path, class name, command, and flag in this curriculum has a date attached in PROGRESS.md §7. Re-verify anything older than about six months.

This is not pedantry. In the course of writing this book, two widely-repeated facts turned out to be retired — the ng_ prefix on Blink layout classes, and TraceWrapperMember<T> for DOM/JS heap synchronisation — and both would have been asserted confidently from memory. Each took under a minute to check in the tree.