diff --git a/docker/macos/Dockerfile b/docker/macos/Dockerfile new file mode 100644 index 0000000..9fab559 --- /dev/null +++ b/docker/macos/Dockerfile @@ -0,0 +1,48 @@ +# DarkRoom โ€” macOS link check +# +# Compiles and links for macOS from Linux, with zig as the linker +# (cargo-zigbuild). Zig carries macOS's libSystem stubs and C headers, so the +# crates that need only libSystem โ€” the inference engine, dr-plat โ€” build, +# link and produce Mach-O test binaries here. Nothing runs: there is no macOS +# to run them on (docs/dev/macos.md ยง2). The desktop app needs Apple's +# framework headers (AppKit, Metal, Security), which only the Xcode SDK +# carries, so it does not link here. +# +# Build: docker build -t darkroom-macos:latest docker/macos +# Use: ./docker/macos/build.sh cargo zigbuild --target aarch64-apple-darwin -p dr-inference-engine --all-targets + +FROM docker.io/library/debian:trixie-slim + +# Pinned, like the Windows and Android images. Rust matches rust-toolchain.toml. +ARG RUST_VERSION=1.92.0 +ARG ZIG_VERSION=0.15.2 +ARG ZIG_SHA256=02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239 +ARG CARGO_ZIGBUILD_VERSION=0.23.4 + +ENV DEBIAN_FRONTEND=noninteractive \ + CARGO_HOME=/opt/cargo \ + RUSTUP_HOME=/opt/rustup \ + PATH=/opt/zig:/opt/cargo/bin:$PATH + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl git xz-utils \ + # A host C compiler: build scripts and proc-macros are Linux binaries. + gcc libc6-dev \ + # `file` says Mach-O; the smoke check in build.sh reads it. + file \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" -o /tmp/zig.tar.xz \ + && echo "${ZIG_SHA256} /tmp/zig.tar.xz" | sha256sum -c - \ + && mkdir /opt/zig && tar xJf /tmp/zig.tar.xz -C /opt/zig --strip-components=1 \ + && rm /tmp/zig.tar.xz && zig version + +# The components rust-toolchain.toml lists, baked in so rustup does not fetch +# them inside every run. +RUN curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal \ + --default-toolchain "${RUST_VERSION}" \ + --component rustfmt,clippy,rust-analyzer \ + --target aarch64-apple-darwin,x86_64-apple-darwin \ + && cargo install --locked "cargo-zigbuild@${CARGO_ZIGBUILD_VERSION}" \ + && rm -rf /opt/cargo/registry \ + && chmod -R a+rwX /opt/cargo /opt/rustup diff --git a/docker/macos/build.sh b/docker/macos/build.sh new file mode 100755 index 0000000..278cd14 --- /dev/null +++ b/docker/macos/build.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Run a command inside the DarkRoom macOS link-check container. +# +# ./docker/macos/build.sh cargo zigbuild --target aarch64-apple-darwin -p dr-inference-engine --all-targets +# ./docker/macos/build.sh # interactive shell +# +# Builds the image on first use; `--rebuild` after editing the Dockerfile. +set -euo pipefail + +IMAGE="darkroom-macos:latest" +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO="$(cd "${HERE}/../.." && pwd)" + +if command -v podman >/dev/null 2>&1; then + ENGINE=podman +elif command -v docker >/dev/null 2>&1; then + ENGINE=docker +else + echo "error: neither podman nor docker found" >&2 + exit 1 +fi + +if [[ "${1:-}" == "--rebuild" ]]; then + shift + "${ENGINE}" build -t "${IMAGE}" "${HERE}" +elif ! "${ENGINE}" image inspect "${IMAGE}" >/dev/null 2>&1; then + echo "==> building ${IMAGE} (first run; a few minutes)" + "${ENGINE}" build -t "${IMAGE}" "${HERE}" +fi + +# Registry, target and zig's own cache persist across runs. +CACHE="${XDG_CACHE_HOME:-${HOME}/.cache}/darkroom-macos" +mkdir -p "${CACHE}/registry" "${CACHE}/target" "${CACHE}/home" + +ARGS=( + --rm + -v "${REPO}:/work:z" + -v "${CACHE}/registry:/opt/cargo/registry:z" + -v "${CACHE}/target:/work/target-macos:z" + -v "${CACHE}/home:/tmp/home:z" + -e HOME=/tmp/home + -e CARGO_TARGET_DIR=/work/target-macos + -w /work +) + +# Capped for the same reason as the Windows image: a cross build otherwise +# takes every thread on the host. +JOBS="${DARKROOM_BUILD_JOBS:-8}" +if [[ "${JOBS}" != "0" ]]; then + ARGS+=(--cpus "${JOBS}" -e "CARGO_BUILD_JOBS=${JOBS}") +fi + +if [[ "${ENGINE}" == "docker" ]]; then + ARGS+=(--user "$(id -u):$(id -g)") +fi + +if [[ $# -eq 0 ]]; then + ARGS+=(-it) + set -- /bin/bash +fi + +exec "${ENGINE}" run "${ARGS[@]}" "${IMAGE}" "$@" diff --git a/docs/dev/macos.md b/docs/dev/macos.md index 812dc9d..899aa80 100644 --- a/docs/dev/macos.md +++ b/docs/dev/macos.md @@ -21,12 +21,20 @@ bundle, and signing. `dr-plat` sends every non-Android Unix to the X11/Wayland d ## 2. Building -The Rust side compile-checks from Linux: +`docker/macos` compiles and links for macOS from Linux, using zig as the linker +(`cargo-zigbuild`). Zig carries libSystem's stubs and the C headers, so tract's SIMD kernels +compile and anything that needs only libSystem links: - rustup target add aarch64-apple-darwin - cargo check --target aarch64-apple-darwin -p dr-inference-engine --features native + ./docker/macos/build.sh cargo zigbuild --target aarch64-apple-darwin -p dr-inference-engine --features native --all-targets + ./docker/macos/build.sh cargo-zigbuild clippy --target aarch64-apple-darwin -p dr-inference-engine --features native --all-targets -- -D warnings -Linking needs Apple's SDK, which means a Mac. On one: +That produces Mach-O arm64 test binaries and the `ladder` and `ep_probe` examples. Nothing runs +them. Anything that links an Apple framework needs the Xcode SDK, which zig does not carry. That +includes `dr-plat` (through the keyring's Security and CoreFoundation) and so the desktop app, and +its link fails with `unable to find framework`. `cargo check` for those still works in the +container. + +Linking the app needs Apple's SDK, which means a Mac. On one: cargo build --profile diagnostic -p darkroom-desktop ./tools/fetch-desktop-runtime.sh # ONNX Runtime 1.29.0 with CoreML, Apple silicon only