build_mac.sh

cross platform rendering playground

build_mac.sh

4.45 KB
#!/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]"