01 — Day-1 macOS Setup (Apple Silicon, macOS Sequoia+ / macOS 16, verified July 2026)¶
Goal. In one focused ~2-hour sitting, take a clean Mac to a working modern C++20/23 dev environment with Homebrew LLVM 21+, CMake 3.28+, Ninja, Conan 2, VSCode, and a green first build.
Commands are copy-pasteable. Read each block before running it. Every version and path is verified for macOS 16 (Sequoia+) on Apple Silicon in July 2026.
§1. Xcode Command Line Tools (5 min)¶
You need Apple’s CLT for the macOS SDK headers and xcrun. You do not
need full Xcode.app.
xcode-select --install
A dialog opens; click Install. Wait ~5 minutes.
Verify:
xcode-select -p
# expected: /Library/Developer/CommandLineTools
xcrun --sdk macosx --show-sdk-path
# expected: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk (or MacOSX16.sdk)
If xcode-select -p prints /Applications/Xcode.app/... you have full
Xcode installed. That’s fine — the rest of this guide still works, but
your SDK path will differ.
§2. Homebrew (5 min)¶
Install Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
On Apple Silicon this installs to /opt/homebrew. The installer prints
two eval lines at the end — run them and add the second to
~/.zshrc:
eval "$(/opt/homebrew/bin/brew shellenv)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc
Verify:
brew --version
# expected: Homebrew 4.x.x (July 2026 baseline)
brew doctor
# expected: Your system is ready to brew.
If brew doctor complains about /usr/local vs /opt/homebrew, you have
a leftover Intel install. Uninstall Intel Homebrew per the official docs
before continuing. This is common on machines migrated from an Intel Mac
via Migration Assistant.
§3. The Day-1 brew bundle (15–20 min)¶
One command installs everything:
brew install llvm cmake ninja git gh conan lldb \
python@3.13 uv jq ripgrep fd fzf bat \
cmake-language-server clang-format
Package roles:
Package |
Role |
Verified version July 2026 |
|---|---|---|
|
clang++ + libc++ + lld + clang-tidy |
21.1.8 |
|
Build-system generator |
3.30+ |
|
Fast build backend |
1.12+ |
|
Newer than macOS system git |
2.47+ |
|
GitHub CLI |
2.60+ |
|
C++ package manager (Phase 4) |
2.x |
|
Newer than Apple lldb |
matches LLVM 21 |
|
Base Python (for |
3.13.x |
|
Astral’s fast Python manager (Phase 5) |
0.5+ |
|
CLI ergonomics |
latest |
|
LSP for CMake in editor |
latest |
|
Only needed if you don’t want to use LLVM’s bundled |
matches LLVM 21 |
Total download: ~2.5 GB. Total on-disk: ~5 GB.
Do not add Homebrew Python to PATH ahead of system Python. uv
handles Python versioning inside projects. Homebrew Python is only here
to bootstrap uv.
§4. PATH setup — brew LLVM MUST win (10 min, critical)¶
macOS ships /usr/bin/clang++. Homebrew installs LLVM 21 to
/opt/homebrew/opt/llvm/bin/clang++. Without a PATH override, the system
clang wins and you get an ancient C++ standard library.
Append to ~/.zshrc:
cat >> ~/.zshrc <<'ZSHRC_APPEND'
# === Homebrew LLVM (brew LLVM must win over Apple Clang) ===
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
# Compiler and linker flags so headers/libs resolve to brew LLVM's libc++
export LDFLAGS="-L/opt/homebrew/opt/llvm/lib/c++ -L/opt/homebrew/opt/llvm/lib/unwind -lunwind"
export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
# Explicit compiler pointers (CMake honors these)
export CC="/opt/homebrew/opt/llvm/bin/clang"
export CXX="/opt/homebrew/opt/llvm/bin/clang++"
# macOS SDK sysroot — refresh when macOS major-updates
export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
# === End Homebrew LLVM block ===
ZSHRC_APPEND
source ~/.zshrc
Verify:
which clang++
# expected: /opt/homebrew/opt/llvm/bin/clang++
clang++ --version
# expected: Homebrew clang version 21.1.8 (or newer)
# Target: arm64-apple-darwin24.x.x (or newer)
# Thread model: posix
# InstalledDir: /opt/homebrew/opt/llvm/bin
If clang++ --version still says Apple clang version ..., your PATH
export is not taking effect. Debug with:
echo $PATH | tr ':' '\n' | head
# Line 1 must be /opt/homebrew/opt/llvm/bin
If line 1 is anything else, another .zshrc line is overriding your
export. Move the brew LLVM block to the very end of ~/.zshrc.
Test C++23 features are available¶
mkdir -p /tmp/cxxtest && cd /tmp/cxxtest
cat > test.cpp <<'CPP'
#include <expected>
#include <print>
#include <ranges>
std::expected<int, std::string> parse(std::string_view s) {
if (s == "42") return 42;
return std::unexpected{"nope"};
}
int main() {
auto r = parse("42");
std::print("value = {}\n", r.value_or(-1));
auto squares = std::views::iota(1, 6)
| std::views::transform([](int x){ return x * x; });
for (int x : squares) std::print("{} ", x);
std::print("\n");
}
CPP
clang++ -std=c++23 -stdlib=libc++ test.cpp -o test
./test
Expected output:
value = 42
1 4 9 16 25
If this compiles and runs, brew LLVM is correctly installed. If
<expected> or <print> is not found, you are still linking against
Apple’s older libc++ — recheck the LDFLAGS, CPPFLAGS, and PATH
in the previous block.
§5. Git and GitHub CLI (10 min)¶
git config --global user.name "Raghul R"
git config --global user.email "your.email@zohocorp.com" # or personal
git config --global init.defaultBranch main
git config --global core.editor "code --wait" # or 'nvim' etc.
git config --global pull.rebase true
git config --global fetch.prune true
git config --global rerere.enabled true
git config --global commit.gpgsign false # enable after §6
Generate an SSH key for GitHub:
ssh-keygen -t ed25519 -C "your.email@zohocorp.com" -f ~/.ssh/id_ed25519
eval "$(ssh-agent -s)"
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Add to macOS SSH config:
mkdir -p ~/.ssh
cat > ~/.ssh/config <<'SSH'
Host github.com
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
SSH
chmod 600 ~/.ssh/config
Authenticate GitHub CLI and upload the key:
gh auth login
# Choose: GitHub.com → HTTPS → Yes (auth Git) → Login with web browser
# It opens your browser; complete the flow.
gh ssh-key add ~/.ssh/id_ed25519.pub --title "M-series MacBook — $(date +%Y-%m-%d)"
Verify:
ssh -T git@github.com
# expected: Hi <username>! You've successfully authenticated ...
Enable 2FA on GitHub via your authenticator app — do this in the browser. Non-negotiable.
§6. Editor: VSCode + extensions (15 min)¶
Install VSCode:
brew install --cask visual-studio-code
Install the extension bundle from CLI:
code --install-extension llvm-vs-code-extensions.vscode-clangd
code --install-extension vadimcn.vscode-lldb
code --install-extension ms-vscode.cmake-tools
code --install-extension twxs.cmake
code --install-extension usernamehw.errorlens
code --install-extension eamodio.gitlens
code --install-extension jeff-hykin.better-cpp-syntax
code --install-extension xaver.clang-format
code --install-extension streetsidesoftware.code-spell-checker
Do NOT install ms-vscode.cpptools (the Microsoft C/C++ extension).
It fights clangd over hover, completion, and diagnostics. clangd
wins for modern C++. Use only clangd for language intelligence.
Full VSCode config (settings.json, tasks.json, launch.json,
.clangd, .clang-format, .clang-tidy) is documented in
02_vscode_config.md. Do that as a follow-up
step; it takes ~20 min and locks in your production config.
§7. First green build — the “Hello, C++23” template (15 min)¶
Create a template project you’ll reuse for every phase:
mkdir -p ~/src/hello-cpp23 && cd ~/src/hello-cpp23
cat > CMakeLists.txt <<'CMAKE'
cmake_minimum_required(VERSION 3.28)
project(hello_cpp23 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Force libc++ (brew LLVM). Apple Clang would default here anyway,
# but we spell it out for reproducibility.
add_compile_options(-stdlib=libc++)
add_link_options(-stdlib=libc++)
# Warnings-as-errors, hardening
add_compile_options(-Wall -Wextra -Wpedantic -Werror
-Wshadow -Wnon-virtual-dtor -Wold-style-cast
-Wcast-align -Woverloaded-virtual -Wconversion
-Wsign-conversion -Wdouble-promotion -Wformat=2)
add_executable(hello src/main.cpp)
CMAKE
mkdir -p src
cat > src/main.cpp <<'CPP'
#include <print>
#include <ranges>
#include <vector>
int main() {
std::print("Hello, C++23 from brew LLVM on Apple Silicon.\n");
auto squares = std::views::iota(1, 6)
| std::views::transform([](int x){ return x * x; });
std::vector<int> v(squares.begin(), squares.end());
for (int x : v) std::print("{} ", x);
std::print("\n");
return 0;
}
CPP
cmake -B build -G Ninja
cmake --build build
./build/hello
Expected:
Hello, C++23 from brew LLVM on Apple Silicon.
1 4 9 16 25
Commit this to a new GitHub repo as your template:
git init && git add -A && git commit -m "Initial: C++23 template with brew LLVM"
gh repo create hello-cpp23 --public --source=. --push
You now own a green C++23 template you can gh repo clone at the start
of every phase.
§8. Troubleshooting (5–30 min per issue)¶
8.1. #include <expected> — file not found¶
You are linking against Apple’s older libc++, not brew’s. Check:
clang++ -stdlib=libc++ -std=c++23 -v -E -x c++ - </dev/null 2>&1 | grep 'c++/v1'
The include search paths must contain
/opt/homebrew/opt/llvm/include/c++/v1 before
/Library/Developer/CommandLineTools/usr/include/c++/v1. If not, your
LDFLAGS/CPPFLAGS from §4 aren’t sourced. Restart your terminal.
8.2. Linker error _main referenced from _start or SDK not found¶
After a macOS point update, the SDK path can shift:
sudo xcode-select --reset
xcode-select --install
export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
Re-open your terminal. Rebuild.
8.3. dyld error at runtime: Library not loaded: @rpath/libc++.1.dylib¶
Brew LLVM’s libc++ isn’t in the loader’s search path. Two fixes:
Preferred (per-project). Add to your CMakeLists.txt:
set(CMAKE_INSTALL_RPATH "/opt/homebrew/opt/llvm/lib/c++")
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
Fallback (env var). In ~/.zshrc:
export DYLD_LIBRARY_PATH="/opt/homebrew/opt/llvm/lib/c++:${DYLD_LIBRARY_PATH:-}"
The env var approach is discouraged in production but fine for a solo learner box.
8.4. Rosetta warning — “arm64 binary running under Rosetta”¶
You likely installed Homebrew in an Intel-emulated shell. Fix:
uname -m
# expected on Apple Silicon: arm64
# if it says x86_64, you're in a Rosetta terminal — exit and open a native one
Find Terminal.app in Finder, Cmd+I, uncheck “Open using Rosetta”, relaunch.
8.5. cmake --build build says “No C++ compiler could be identified”¶
CMake is not seeing your CXX. Try:
cmake -B build -G Ninja -DCMAKE_CXX_COMPILER=/opt/homebrew/opt/llvm/bin/clang++
Then permanently: verify §4 export CXX= line is present in ~/.zshrc
AND that you sourced it.
8.6. brew install llvm extremely slow or times out¶
You are being rate-limited by GitHub or CDN. Try:
brew install --HEAD llvm # avoids GHCR bottle cache
# OR
HOMEBREW_NO_INSTALL_FROM_API=1 brew install llvm
Or wait for a lower-traffic time window (IST 07:00–10:00 is fastest from India).
8.7. VSCode’s clangd shows red squigglies but cmake --build succeeds¶
clangd needs compile_commands.json. Two fixes:
Ensure
CMAKE_EXPORT_COMPILE_COMMANDS ONinCMakeLists.txt(already set in §7).Symlink it to the project root:
ln -sf build/compile_commands.json compile_commands.json
Restart
clangd:Cmd+Shift+P→clangd: Restart language server.
Full clangd config is in 02_vscode_config.md.
§9. Verification checklist — sign off Day 1¶
Copy this checklist. If every line is a green ✅ you are done.
xcode-select -pprints a valid CLT pathbrew doctorsays “ready to brew”which clang++shows/opt/homebrew/opt/llvm/bin/clang++clang++ --versionshows Homebrew clang 21.x or newercmake --versionshows 3.28+ninja --versionshows 1.12+git config --global user.emailshows the right addressssh -T git@github.comsays “successfully authenticated”gh auth statusshows logged inhellobinary from §7 prints correctlyhello-cpp23template is pushed to your GitHub~/.zshrchas the LLVM block AND the brew shellenv lineTerminal.app is NOT running under Rosetta (
uname -m→arm64)
If all boxes are green, you have a working environment for Phase 0–7.
§10. What you did NOT do today¶
You did not install CLion. That’s
03_clion_alternative.md. Optional.You did not install Docker/OrbStack. That’s
04_docker_and_devcontainers.md. Needed by Phase 4.You did not set up cloud GPU. That’s
05_cloud_and_gpu_access.md. Needed by Phase 5.You did not configure VSCode dotfiles beyond installing extensions. That’s
02_vscode_config.md. Do it Day 2 (30 min).You did not install vcpkg. Deliberate — wait until Phase 4.