#!/usr/bin/env bash
# Metal build for the RHI examples (macOS only; main renderer not supported yet).
# Usage: build_mac.sh [example] [-r] [-c]
# example Name under src/backend/conformance/ (default: triangle).
# Must contain <example>/<example>.cpp.
# -r Release build (default is Debug)
# -c Clean build (delete the example objects first)
# All build artifacts and output live under build/.
# The binary must run from the repo root (shader paths are repo-relative).
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
build_type=debug
clean_build=0
example="triangle"
for arg in "$@"; do
case "$arg" in
-r|release) build_type=release ;;
-c) clean_build=1 ;;
*) example="$arg" ;;
esac
done
echo "[$build_type metal build: $example]"
ROOT="$SCRIPT_DIR"
EXAMPLES="$ROOT/src/backend/conformance"
EXAMPLE_SRC="$EXAMPLES/$example/$example.cpp"
if [ ! -f "$EXAMPLE_SRC" ]; then
echo "[error: no such example: $example (expected $EXAMPLE_SRC)]" >&2
exit 1
fi
BUILD="$ROOT/build/metal-$example"
OBJ="$ROOT/build/obj-ex-$example"
DEPS="$ROOT/build/deps"
if [ "$clean_build" = "1" ]; then
echo "[cleaning metal build]"
rm -rf "$OBJ"
fi
mkdir -p "$OBJ" "$DEPS" "$BUILD"
# --- Download Slang (binary SDK, shared across examples) --------------------
SLANG_DIR="$DEPS/slang"
if [ ! -f "$SLANG_DIR/lib/libslang.dylib" ]; then
echo "[downloading slang]"
mkdir -p "$SLANG_DIR"
SLANG_URL="https://github.com/shader-slang/slang/releases/download/v2026.17.1/slang-2026.17.1-macos-aarch64.tar.gz"
curl -sL "$SLANG_URL" -o "$DEPS/slang.tar.gz"
tar -xzf "$DEPS/slang.tar.gz" -C "$SLANG_DIR" --strip-components=1
rm "$DEPS/slang.tar.gz"
else
echo "[slang already downloaded]"
fi
SLANG_INCLUDE="-I$SLANG_DIR/include"
SLANG_LINK="-L$SLANG_DIR/lib -lslang -Wl,-rpath,$SLANG_DIR/lib"
# --- glfw (static, shared across examples) ----------------------------------
if [ ! -f "$DEPS/libglfw3.a" ]; then
echo "[building glfw]"
mkdir -p "$DEPS/glfw"
cmake -G Ninja -S "$ROOT/third-party/glfw" -B "$DEPS/glfw" \
-DGLFW_BUILD_DOCS=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_EXAMPLES=OFF \
-DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release >/dev/null 2>&1 || exit 1
cmake --build "$DEPS/glfw" >/dev/null 2>&1 || exit 1
cp "$DEPS/glfw/src/libglfw3.a" "$DEPS/libglfw3.a"
else
echo "[glfw already built]"
fi
# --- Compile flags ----------------------------------------------------------
CXX="clang++"
CXX_COMMON="-std=c++20 -fno-rtti -DRHI_BACKEND_METAL -DFMT_USE_CONSTEXPR=0 -DFMT_ENFORCE_COMPILE_STRING=0"
CXX_INCLUDES="-I$ROOT/src -I$ROOT/third-party -I$ROOT/third-party/glfw/include -I$ROOT/third-party/plog/include -I$ROOT/third-party/rtm/includes -I$ROOT/third-party/metal-cpp $SLANG_INCLUDE"
if [ "$build_type" = "debug" ]; then
CXX_FLAGS="-g -O0 -D_DEBUG $CXX_COMMON $CXX_INCLUDES"
else
CXX_FLAGS="-g -O2 -DNDEBUG $CXX_COMMON $CXX_INCLUDES"
fi
# --- Source files -----------------------------------------------------------
SRC="$ROOT/src"
SOURCES="\
$EXAMPLE_SRC \
$SRC/core/arena.cpp \
$SRC/core/platform.cpp \
$SRC/core/logger.cpp \
$SRC/core/math_rtm.cpp \
$SRC/backend/metal/metal_impl.cpp \
$SRC/backend/metal/device.cpp \
$SRC/backend/metal/swapchain.cpp \
$SRC/backend/metal/cmd.cpp \
$SRC/backend/metal/arguments_table.cpp \
$SRC/backend/metal/image.cpp \
$SRC/backend/metal/sampler.cpp \
$SRC/backend/metal/shader.cpp \
$SRC/backend/metal/buffer.cpp \
$SRC/backend/metal/pipeline.cpp \
$SRC/backend/metal/metal_platform.mm"
# --- Compile ----------------------------------------------------------------
echo "[compiling]"
for src in $SOURCES; do
objname=$(basename "$src" | sed 's/\.mm$/.o/;s/\.cpp$/.o/')
case "$src" in
*.mm) $CXX -fobjc-arc -c $CXX_FLAGS "$src" -o "$OBJ/$objname" ;;
*) $CXX -c $CXX_FLAGS "$src" -o "$OBJ/$objname" ;;
esac
done
# --- Link -------------------------------------------------------------------
echo "[linking]"
$CXX -fobjc-arc "$OBJ"/*.o \
"$DEPS/libglfw3.a" \
$SLANG_LINK \
-framework Metal -framework QuartzCore -framework Cocoa -framework Foundation \
-framework IOKit -framework CoreVideo \
-lpthread \
-o "$BUILD/metal-$example"
echo "[done: $BUILD/metal-$example]"
echo "[run from the repo root: ./build/metal-$example/metal-$example]"