Setting up on macOS (primary platform)¶
This is the main supported setup: an Apple Silicon Mac (M1 or newer) with Homebrew. Intel Macs work too, because Homebrew lives in /usr/local there and the build finds it automatically.
You will install:
| What | Why | From |
|---|---|---|
LLVM 23 (llvm) |
headers, libraries, CMake config, opt/llc/lli/FileCheck, and the clang we compile with |
Homebrew |
uv |
manages the course's Python environment (lit, PyYAML, the website) from uv.lock |
Homebrew |
googletest |
the unit-test framework | Homebrew |
ninja, cmake |
the build tools | Homebrew |
| Python ≥ 3.11 | the ./course CLI, lit, the course website |
uv installs one if needed |
1. Install the tools¶
You need the Xcode Command Line Tools for the macOS SDK and linker. You do not need full Xcode.
xcode-select --install # skip this if it says the tools are already installed
brew update
brew install llvm uv googletest ninja cmake
Check that Homebrew's LLVM is version 23:
If it prints an older version, run brew upgrade llvm. If Homebrew has moved on to LLVM 24 or later, install the versioned formula instead (brew install llvm@23) and pass -DPEBBLE_LLVM_PREFIX=$(brew --prefix llvm@23) in step 3.
2. Why Homebrew clang and not Apple clang¶
macOS ships its own clang (Apple clang, in /usr/bin). Don't use it for this course.
- Apple clang is a fork with its own versioning ("Apple clang 17") and its own libc++. It does not match LLVM 23 exactly.
- Your pass plugins are loaded into Homebrew's
opt. If a plugin is built with one toolchain and loaded into a tool built with another, the C++ ABI and symbol details can differ. That leads to crashes at load time, or "Symbol not found" errors. - The course needs C++23 (
std::expected,std::print), and Homebrew clang 23 has complete support.
The macos CMake preset handles this for you. It uses cmake/toolchains/homebrew-llvm.cmake, which finds $(brew --prefix llvm) and sets CMAKE_CXX_COMPILER to Homebrew's clang++. If Apple clang is ever picked up, configuration fails on purpose with instructions to fix it.
You do not need to change your PATH. Adding $(brew --prefix llvm)/bin to PATH is optional. It's convenient when you want to run opt or FileCheck by hand, but it isn't required.
3. Configure, build, test¶
From the repository root:
uv sync # .venv with the pinned lit, PyYAML and website tools (uv.lock)
cmake --preset macos # configure into build/macos
cmake --build --preset macos # compile
ctest --preset macos # run all tests (skeleton: most chapters fail with TODO)
uv sync creates .venv/ from pyproject.toml + uv.lock (exact versions, including lit 23.1.2), and CMake prefers .venv when it exists. cmake --build build/macos --target setup-venv runs the same uv sync. You never need to activate the environment: ./course runs itself through uv run, and uv run <cmd> runs any other tool in it.
Configuration prints a summary, and also writes it to build/macos/pebble-env.txt:
-- Pebble: compiler Clang 23.1.x (/opt/homebrew/opt/llvm/bin/clang++)
-- Pebble: LLVM 23.1.x from /opt/homebrew/opt/llvm/lib/cmake/llvm
-- Pebble: FileCheck: /opt/homebrew/opt/llvm/bin/FileCheck
-- Pebble: GoogleTest installed 1.17.0 (...)
4. Verify the installation¶
This runs the infrastructure self-test (tests/infra/). infra.lit and infra.TodoTest.* must pass. They prove that the pass plugin loads into opt, and that lit, FileCheck and GoogleTest work.
The infra.InfraDemoTest.* tests fail with TODO(infra): .... That is intended: it's what an unimplemented exercise looks like. To see them pass, build with the solution:
cmake --preset macos -DPEBBLE_USE_SOLUTION=infra-demo && cmake --build --preset macos
ctest --preset macos -L infra # now all pass
cmake --preset macos -DPEBBLE_USE_SOLUTION= # back to your own code
Finally, run ./course doctor. It reads build/<preset>/pebble-env.txt and checks everything.
Everyday commands¶
| Task | Command |
|---|---|
| Run one chapter's tests | cmake --build build/macos --target check-ch15 or ctest --preset macos -L '^ch15$' |
| Unit tests only / lit only | ctest --preset macos -L unit / -L lit |
| Run a single lit test | build/macos/bin/pebble-lit -v tests/ch15/lit/some-test.ll |
| Use the reference solution for some components | cmake --preset macos -DPEBBLE_USE_SOLUTION=lexer,parser |
| Debug build | cmake --preset macos-debug (then use macos-debug for build/test too) |
If something goes wrong, see troubleshooting.md.