# syntax=docker/dockerfile:1.6 # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # Cross-compile the openshell CLI binary for macOS aarch64 (Apple Silicon) # using the osxcross toolchain. Produces a standalone binary — no Python # wheel wrapping. # # Usage: # docker buildx build -f deploy/docker/Dockerfile.cli-macos \ # --build-arg OPENSHELL_CARGO_VERSION=0.6.0 \ # --output type=local,dest=out/ . ARG OSXCROSS_IMAGE=ghcr.io/crazy-max/osxcross:latest ARG PYTHON_IMAGE=public.ecr.aws/docker/library/python:3.12-slim FROM ${OSXCROSS_IMAGE} AS osxcross FROM ${PYTHON_IMAGE} AS builder ARG CARGO_TARGET_CACHE_SCOPE=default ENV PATH="/root/.cargo/bin:/usr/local/bin:/osxcross/bin:${PATH}" ENV LD_LIBRARY_PATH="/osxcross/lib" COPY --from=osxcross /osxcross /osxcross RUN SDKROOT="$(echo /osxcross/SDK/MacOSX*.sdk)" && ln -sfn "${SDKROOT}" /osxcross/SDK/MacOSX.sdk RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ ca-certificates \ clang \ cmake \ curl \ libclang-dev \ pkg-config \ && rm -rf /var/lib/apt/lists/* RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.95.0 # aws-lc-sys probes with --target=arm64-apple-macosx and clang then looks for # arm64-apple-macosx-ld. Provide a linker alias to osxcross ld64. RUN ln -sf /osxcross/bin/arm64-apple-darwin25.1-ld /usr/local/bin/arm64-apple-macosx-ld RUN rustup target add aarch64-apple-darwin WORKDIR /build ENV CC_aarch64_apple_darwin=oa64-clang ENV CXX_aarch64_apple_darwin=oa64-clang++ ENV AR_aarch64_apple_darwin=aarch64-apple-darwin25.1-ar ENV CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=oa64-clang ENV CARGO_TARGET_AARCH64_APPLE_DARWIN_AR=aarch64-apple-darwin25.1-ar ENV SDKROOT=/osxcross/SDK/MacOSX.sdk ENV MACOSX_DEPLOYMENT_TARGET=13.3 ENV CFLAGS_aarch64_apple_darwin=--target=arm64-apple-macosx\ -mmacosx-version-min=13.3 ENV CXXFLAGS_aarch64_apple_darwin=--target=arm64-apple-macosx\ -mmacosx-version-min=13.3 ENV BINDGEN_EXTRA_CLANG_ARGS_aarch64_apple_darwin=--target=arm64-apple-macosx\ -isysroot\ ${SDKROOT} # --------------------------------------------------------------------------- # Stage 1: dependency caching — copy only manifests, create dummy sources, # build dependencies. This layer is cached unless Cargo.toml/lock changes. # --------------------------------------------------------------------------- COPY Cargo.toml Cargo.lock ./ COPY crates/openshell-bootstrap/Cargo.toml crates/openshell-bootstrap/Cargo.toml COPY crates/openshell-cli/Cargo.toml crates/openshell-cli/Cargo.toml COPY crates/openshell-core/Cargo.toml crates/openshell-core/Cargo.toml COPY crates/openshell-policy/Cargo.toml crates/openshell-policy/Cargo.toml COPY crates/openshell-providers/Cargo.toml crates/openshell-providers/Cargo.toml COPY crates/openshell-tui/Cargo.toml crates/openshell-tui/Cargo.toml COPY crates/openshell-prover/Cargo.toml crates/openshell-prover/Cargo.toml COPY crates/openshell-core/build.rs crates/openshell-core/build.rs COPY proto/ proto/ # Scope workspace to CLI crates only to avoid compiling unrelated runtimes. RUN sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-cli", "crates/openshell-core", "crates/openshell-bootstrap", "crates/openshell-policy", "crates/openshell-prover", "crates/openshell-providers", "crates/openshell-tui"]|' Cargo.toml RUN mkdir -p crates/openshell-cli/src \ crates/openshell-core/src \ crates/openshell-bootstrap/src \ crates/openshell-policy/src \ crates/openshell-providers/src \ crates/openshell-prover/src \ crates/openshell-tui/src && \ echo "fn main() {}" > crates/openshell-cli/src/main.rs && \ touch crates/openshell-core/src/lib.rs && \ touch crates/openshell-bootstrap/src/lib.rs && \ touch crates/openshell-policy/src/lib.rs && \ touch crates/openshell-providers/src/lib.rs && \ touch crates/openshell-prover/src/lib.rs && \ touch crates/openshell-tui/src/lib.rs RUN --mount=type=cache,id=cargo-registry-cli-macos,sharing=locked,target=/root/.cargo/registry \ --mount=type=cache,id=cargo-git-cli-macos,sharing=locked,target=/root/.cargo/git \ --mount=type=cache,id=cargo-target-cli-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ cargo build --release --target aarch64-apple-darwin -p openshell-cli 2>/dev/null || true # --------------------------------------------------------------------------- # Stage 2: real build # --------------------------------------------------------------------------- COPY crates/ crates/ COPY providers/ providers/ # Touch source files to ensure they're rebuilt (not the cached dummy). RUN touch crates/openshell-cli/src/main.rs \ crates/openshell-cli/src/lib.rs \ crates/openshell-bootstrap/src/lib.rs \ crates/openshell-core/src/lib.rs \ crates/openshell-policy/src/lib.rs \ crates/openshell-providers/src/lib.rs \ crates/openshell-tui/src/lib.rs \ crates/openshell-core/build.rs \ proto/*.proto # Declare version ARGs here (not earlier) so the git-hash-bearing values do not # invalidate the expensive dependency-build layers above on every commit. ARG OPENSHELL_CARGO_VERSION ARG OPENSHELL_IMAGE_TAG RUN --mount=type=cache,id=cargo-registry-cli-macos,sharing=locked,target=/root/.cargo/registry \ --mount=type=cache,id=cargo-git-cli-macos,sharing=locked,target=/root/.cargo/git \ --mount=type=cache,id=cargo-target-cli-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ fi && \ cargo build --release --target aarch64-apple-darwin -p openshell-cli && \ cp target/aarch64-apple-darwin/release/openshell /openshell FROM scratch AS binary COPY --from=builder /openshell /openshell