# ============================================================================= # WZ Phone — Windows (x86_64-pc-windows-msvc) cross-compile image # # Cross-compiles the Tauri desktop binary for Windows from a Linux host via # `cargo xwin`, which auto-downloads the Microsoft CRT + Windows SDK at build # time. This image pre-warms that cache so the cross-compile is as close as # possible to a native Linux build on rebuild (~3 min warm vs ~20 min cold). # # Build: # docker build -t wzp-windows-builder -f Dockerfile.windows-builder . # # Run: driven by scripts/build-windows-docker.sh (see that file). # ============================================================================= FROM debian:bookworm ARG RUST_TARGET=x86_64-pc-windows-msvc ENV DEBIAN_FRONTEND=noninteractive # ── System packages ────────────────────────────────────────────────────────── # - build-essential + pkg-config + libssl-dev: baseline cargo build toolchain # - cmake + ninja-build: audiopus_sys (libopus) uses cmake and expects Ninja # as the generator for the windows target; without ninja-build the cmake # build fails with "CMake was unable to find a build program corresponding # to Ninja" partway through. # - llvm + clang + lld: cargo-xwin uses clang + lld-link for PE/COFF output. # - nasm: ring / rustls assembly for Windows needs NASM on non-Windows hosts. # - curl, git, ca-certificates, unzip: obvious plumbing. # - xz-utils: some Microsoft installer archives are xz-compressed. RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cmake \ ninja-build \ curl \ git \ pkg-config \ libssl-dev \ ca-certificates \ llvm \ clang \ lld \ nasm \ unzip \ xz-utils \ file \ && rm -rf /var/lib/apt/lists/* # ── Node.js 20 LTS (required by Tauri for frontend build) ──────────────────── RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ && rm -rf /var/lib/apt/lists/* \ && node --version \ && npm --version # ── Builder user (1000:1000) — matches host bind-mount UID for the cache # volumes so cargo-registry / target survive across runs without perms # gymnastics. RUN groupadd -g 1000 builder \ && useradd -m -u 1000 -g 1000 -s /bin/bash builder USER builder WORKDIR /home/builder # ── Rust toolchain + Windows target + cargo-xwin ──────────────────────────── RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ | sh -s -- -y --default-toolchain stable \ && . $HOME/.cargo/env \ && rustup target add ${RUST_TARGET} \ && cargo install cargo-xwin --locked ENV PATH="/home/builder/.cargo/bin:$PATH" \ XWIN_ACCEPT_LICENSE=1 \ RUST_TARGET_WIN=${RUST_TARGET} # ── Pre-warm the xwin cache ───────────────────────────────────────────────── # cargo-xwin downloads the Microsoft CRT + Windows SDK (~1.5-2 GB) into # ~/.cache/cargo-xwin the first time it runs. Baking that into an image # layer saves ~4 minutes off every subsequent cold run. # # We do this by creating a throwaway Rust project, building it with # cargo-xwin against the Windows target, then deleting the project but # keeping the xwin cache. RUN set -eux; \ mkdir -p /tmp/xwin-warmup && cd /tmp/xwin-warmup && \ . $HOME/.cargo/env && \ cargo new --bin xwin-warmup --quiet && \ cd xwin-warmup && \ cargo xwin build --release --target ${RUST_TARGET} 2>&1 | tail -5 && \ cd / && rm -rf /tmp/xwin-warmup && \ du -sh $HOME/.cache/cargo-xwin # ── Patch cargo-xwin's override.cmake to inject SSE4.1 / SSSE3 ──────────── # libopus (via audiopus_sys) uses per-file COMPILE_FLAGS "-msse4.1" on # its opus/silk/x86/*_sse4_1.c sources, but clang-cl silently drops # bare -m flags (it expects /clang:-m… instead). The per-file intrinsic # functions then fail to compile because the containing function isn't # marked with the target feature. # # Previously tried patching CMAKE_C_FLAGS via the toolchain file — that # didn't stick because cmake-rs passes `-DCMAKE_C_FLAGS=` from the # command line, and its assembly of that value happens before the # toolchain's `set(... FORCE)` in the cache lookup path; the net effect # was that the toolchain patch never propagated into the final # CMakeCache.txt for the opus subbuild. # # The reliable fix is to inject the SSE flags directly into the # `CMAKE_C_COMPILE_OBJECT` command template via # `CMAKE_USER_MAKE_RULES_OVERRIDE` (which cargo-xwin already uses for # an unrelated clang-cl quirk). The command template is the string # cmake uses to build each compile command line, and manipulating it # with `string(REPLACE "" " /clang:-msse4.1 …" …)` # puts the flags into every C compile invocation without touching # CMAKE_C_FLAGS at all — it's the CMake equivalent of a compiler # wrapper. # # Baked into the image so the patch lives alongside cargo-xwin's own # override.cmake edits, and survives across container runs. RUN set -eux; \ OVERRIDE=$HOME/.cache/cargo-xwin/cmake/clang-cl/override.cmake; \ test -f "$OVERRIDE"; \ grep -q WZP_SSE_PATCH "$OVERRIDE" || \ cat >> "$OVERRIDE" << 'SSE_PATCH_EOF' ; \ # WZP_SSE_PATCH — force SSE4.1 / SSSE3 on every C / C++ compile so the # libopus (audiopus_sys) sse4_1.c / ssse3.c sources link their # _mm_cvtepi16_epi32 / _mm_mul_epi32 / _mm_blend_epi16 intrinsics. # Replaces the placeholder in the CMAKE_C_COMPILE_OBJECT / # CMAKE_CXX_COMPILE_OBJECT command templates with the same placeholder # followed by four /clang:-m* flags, so the flags end up in every # compile command line without ever touching CMAKE_C_FLAGS. string(REPLACE "" " /clang:-msse4.1 /clang:-mssse3 /clang:-msse3 /clang:-msse2" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}") string(REPLACE "" " /clang:-msse4.1 /clang:-mssse3 /clang:-msse3 /clang:-msse2" CMAKE_CXX_COMPILE_OBJECT "${CMAKE_CXX_COMPILE_OBJECT}") SSE_PATCH_EOF echo "=== Patched override.cmake tail ==="; \ tail -12 "$OVERRIDE" WORKDIR /build/source