# Dockerfile for cross-compiling mecha10 robot projects
#
# FAST BUILDS: Framework nodes (@mecha10/*) are NOT compiled here.
# They are downloaded as pre-built binaries at runtime by the robot binary.
# This dramatically reduces build time (from ~15min to ~2min).
#
# What gets compiled:
# - The thin orchestrator binary
# - Custom nodes (if any in your project)
# - mecha10-node-resolver (for runtime node downloads)
#
# What does NOT get compiled (downloaded at runtime instead):
# - @mecha10/speaker, @mecha10/motor, @mecha10/teleop, etc.
#
# Supports multiple architectures:
# - x86_64 (linux/amd64) - Desktop, servers
# - aarch64 (linux/arm64) - Raspberry Pi 4/5, Jetson, Apple Silicon
#
# Uses cargo-chef for dependency caching (faster rebuilds)
# See docs/guides/FASTER_BUILDS.md
#
# Usage:
# # Build for x86_64 (default)
# docker build -f docker/robot-builder.Dockerfile -t {{project_name}}-builder .
#
# # Build for aarch64
# docker build -f docker/robot-builder.Dockerfile \
# --build-arg TARGET_ARCH=aarch64 \
# --platform linux/arm64 \
# -t {{project_name}}-builder .
#
# # Extract binary
# docker create --name extract {{project_name}}-builder
# docker cp extract:/output/{{project_name}} ./dist/{{project_name}}
# docker cp extract:/output/mecha10.json ./dist/mecha10.json
# docker rm extract
#
# The binary will be at ./dist/{{project_name}}
# syntax=docker/dockerfile:1
ARG TARGET_ARCH=x86_64
# ==============================================================================
# Chef stage - install cargo-chef
# ==============================================================================
FROM --platform=$BUILDPLATFORM rustlang/rust:nightly-bookworm AS chef
ARG TARGET_ARCH
WORKDIR /build
# Install minimal dependencies (no heavy ML libs needed - nodes are pre-built)
RUN apt-get update && apt-get install -y \
pkg-config \
libssl-dev \
mold \
&& rm -rf /var/lib/apt/lists/*
# Install cross-compilation toolchain for aarch64 if needed
RUN if [ "$TARGET_ARCH" = "aarch64" ]; then \
dpkg --add-architecture arm64 && \
apt-get update && apt-get install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
libssl-dev:arm64 \
&& rm -rf /var/lib/apt/lists/* \
&& rustup target add aarch64-unknown-linux-gnu; \
fi
# Set up cross-compilation environment for aarch64
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
ENV PKG_CONFIG_ALLOW_CROSS=1
ENV PKG_CONFIG_PATH_aarch64_unknown_linux_gnu=/usr/lib/aarch64-linux-gnu/pkgconfig
# Install cargo-chef
RUN cargo install cargo-chef
# ==============================================================================
# Planner stage - generate dependency recipe
# ==============================================================================
FROM chef AS planner
# Copy project files for recipe generation
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
COPY build.rs ./
RUN cargo chef prepare --recipe-path recipe.json
# ==============================================================================
# Builder stage - compile the Rust binary
# ==============================================================================
FROM chef AS builder
ARG TARGET_ARCH
ARG TARGETPLATFORM
# Copy recipe and build dependencies only (this layer is cached)
COPY --from=planner /build/recipe.json recipe.json
# Build dependencies - this is cached unless Cargo.toml/lock changes
# FAST: Only builds mecha10-core, mecha10-node-resolver, and standard libs
# NO framework nodes compiled (they're downloaded at runtime)
RUN --mount=type=cache,target=/usr/local/cargo/registry \
if [ "$TARGET_ARCH" = "aarch64" ]; then \
cargo chef cook --release --recipe-path recipe.json --target aarch64-unknown-linux-gnu; \
else \
cargo chef cook --release --recipe-path recipe.json; \
fi
# Copy project files
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
COPY build.rs ./
COPY mecha10.json ./
# Copy optional directories if they exist (will be skipped if not present)
COPY behavior[s]/ behaviors/
COPY config[s]/ configs/
COPY node[s]/ nodes/
# Build for robot target with release optimizations
# FAST: Only compiles thin orchestrator + custom nodes
# Framework nodes are NOT compiled - they're downloaded at runtime
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/build/target \
if [ "$TARGET_ARCH" = "aarch64" ]; then \
cargo build --release --no-default-features --features target-robot \
--target aarch64-unknown-linux-gnu && \
mkdir -p /output && \
cp /build/target/aarch64-unknown-linux-gnu/release/{{project_name}} /output/; \
else \
cargo build --release --no-default-features --features target-robot && \
mkdir -p /output && \
cp /build/target/release/{{project_name}} /output/; \
fi
# Copy mecha10.json and configs to output
RUN cp /build/mecha10.json /output/ && \
if [ -d /build/configs ]; then cp -r /build/configs /output/; fi
# ==============================================================================
# Output stage - minimal image with just the binary
# ==============================================================================
FROM debian:bookworm-slim
ARG TARGET_ARCH
# Minimal runtime dependencies
# Note: Framework nodes have their own dependencies and are downloaded separately
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
&& rm -rf /var/lib/apt/lists/*
# Copy binary and config from builder
COPY --from=builder /output/ /output/
# For runtime use (optional - mainly for extraction)
WORKDIR /output
RUN chmod +x /output/{{project_name}}
# Note: On first run, the binary will download framework nodes to ~/.mecha10/bin/
# This happens automatically and nodes are cached for subsequent runs.
ENTRYPOINT ["/output/{{project_name}}"]