feat: add multi-platform Docker build system for Linux, macOS, and Windows

Replace the single Ubuntu-based container build with a dedicated
Dockerfile, build script, and launcher for each target platform.

Infrastructure:
- Add .dockerignore to minimize Docker build context
- Add container/builder-linux.Dockerfile (Ubuntu 24.04, GCC, native build)
- Add container/builder-macos.Dockerfile (multi-stage: SDK fetcher + osxcross/Clang 18)
- Add container/builder-windows.Dockerfile (LLVM-MinGW 20240619, vcpkg)
- Add container/macos/sdk-fetcher.py (auto-fetch macOS SDK from Apple catalog)
- Add container/macos/osxcross-toolchain.cmake (auto-detecting CMake toolchain)
- Add container/macos/triplets/arm64-osx-cross.cmake
- Add container/macos/triplets/x64-osx-cross.cmake
- Remove container/builder-ubuntu.Dockerfile (replaced by per-platform Dockerfiles)
- Remove container/build-in-container.sh and container/build-wowee.sh (replaced)

Build scripts (run inside containers):
- Add container/build-linux.sh (tar copy, FidelityFX clone, cmake/ninja)
- Add container/build-macos.sh (arch detection, vcpkg triplet, cross-compile)
- Add container/build-windows.sh (Vulkan import lib via dlltool, cross-compile)

Launcher scripts (run on host):
- Add container/run-linux.sh, run-macos.sh, run-windows.sh (bash)
- Add container/run-linux.ps1, run-macos.ps1, run-windows.ps1 (PowerShell)

Documentation:
- Add container/README.md (quick start, options, file structure, troubleshooting)
- Add container/FLOW.md (comprehensive build flow for each platform)

CMake changes:
- Add macOS cross-compile support (VulkanHeaders, -undefined dynamic_lookup)
- Add LLVM-MinGW/Windows cross-compile support
- Detect osxcross toolchain and vcpkg triplets

Other:
- Update vcpkg.json with ffmpeg feature flags
- Update resources/wowee.rc version string
This commit is contained in:
Paul 2026-03-30 20:17:41 +03:00
parent c1c28d4216
commit 85f8d05061
25 changed files with 1881 additions and 74 deletions

50
.dockerignore Normal file
View file

@ -0,0 +1,50 @@
# .dockerignore — Exclude files from the Docker build context.
# Keeps the context small and prevents leaking build artifacts or secrets.
# Build outputs
build/
cache/
# Git history
.git/
.gitignore
.github/
# Large external directories (fetched at build time inside the container)
extern/FidelityFX-FSR2/
extern/FidelityFX-SDK/
# IDE / editor files
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Documentation (not needed for build)
docs/
*.md
!container/*.md
# Test / tool outputs
logs/
# Host build scripts that run outside the container (not needed inside)
build.sh
build.bat
build.ps1
rebuild.sh
rebuild.bat
rebuild.ps1
clean.sh
debug_texture.*
extract_assets.*
extract_warden_rsa.py
restart-worldserver.sh
test.sh
# macOS SDK tarballs that may be temporarily placed here
*.tar.xz
*.tar.gz
*.tar.bz2

View file

@ -248,28 +248,98 @@ endif()
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
find_package(Vulkan QUIET) find_package(Vulkan QUIET)
if(NOT Vulkan_FOUND) if(NOT Vulkan_FOUND)
# Fallback: some distros / CMake versions need pkg-config to locate Vulkan. # For Windows cross-compilation the host pkg-config finds the Linux libvulkan-dev
find_package(PkgConfig QUIET) # and injects /usr/include as an INTERFACE_INCLUDE_DIRECTORY, which causes
if(PkgConfig_FOUND) # MinGW clang to pull in glibc headers (bits/libc-header-start.h) instead of
pkg_check_modules(VULKAN_PKG vulkan) # the MinGW sysroot headers. Skip the host pkg-config path entirely and instead
if(VULKAN_PKG_FOUND) # locate Vulkan via vcpkg-installed vulkan-headers or the MinGW toolchain.
add_library(Vulkan::Vulkan INTERFACE IMPORTED) if(CMAKE_CROSSCOMPILING AND WIN32)
set_target_properties(Vulkan::Vulkan PROPERTIES # The cross-compile build script generates a Vulkan import library
INTERFACE_INCLUDE_DIRECTORIES "${VULKAN_PKG_INCLUDE_DIRS}" # (libvulkan-1.a) in ${CMAKE_BINARY_DIR}/vulkan-import from the headers.
INTERFACE_LINK_LIBRARIES "${VULKAN_PKG_LIBRARIES}" set(_VULKAN_IMPORT_DIR "${CMAKE_BINARY_DIR}/vulkan-import")
)
if(VULKAN_PKG_LIBRARY_DIRS) find_package(VulkanHeaders CONFIG QUIET)
if(VulkanHeaders_FOUND)
if(NOT TARGET Vulkan::Vulkan)
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
endif()
# Vulkan::Headers is provided by vcpkg's vulkan-headers port and carries
# the correct MinGW include path — no Linux system headers involved.
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Vulkan::Headers)
# Link against the Vulkan loader import library (vulkan-1.dll).
if(EXISTS "${_VULKAN_IMPORT_DIR}/libvulkan-1.a")
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
INTERFACE_LINK_DIRECTORIES "${VULKAN_PKG_LIBRARY_DIRS}") INTERFACE_LINK_DIRECTORIES "${_VULKAN_IMPORT_DIR}")
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
INTERFACE_LINK_LIBRARIES vulkan-1)
endif() endif()
set(Vulkan_FOUND TRUE) set(Vulkan_FOUND TRUE)
message(STATUS "Found Vulkan via pkg-config: ${VULKAN_PKG_LIBRARIES}") message(STATUS "Found Vulkan headers for Windows cross-compile via vcpkg VulkanHeaders")
else()
# Last-resort: check the LLVM-MinGW toolchain sysroot directly.
find_path(_VULKAN_MINGW_INCLUDE NAMES vulkan/vulkan.h
PATHS /opt/llvm-mingw/x86_64-w64-mingw32/include NO_DEFAULT_PATH)
if(_VULKAN_MINGW_INCLUDE)
if(NOT TARGET Vulkan::Vulkan)
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
endif()
set_target_properties(Vulkan::Vulkan PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_VULKAN_MINGW_INCLUDE}")
# Link against the Vulkan loader import library (vulkan-1.dll).
if(EXISTS "${_VULKAN_IMPORT_DIR}/libvulkan-1.a")
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
INTERFACE_LINK_DIRECTORIES "${_VULKAN_IMPORT_DIR}")
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
INTERFACE_LINK_LIBRARIES vulkan-1)
endif()
set(Vulkan_FOUND TRUE)
message(STATUS "Found Vulkan headers in LLVM-MinGW sysroot: ${_VULKAN_MINGW_INCLUDE}")
endif()
endif()
elseif(CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# macOS cross-compilation: use vcpkg-installed vulkan-headers.
# The host pkg-config would find Linux libvulkan-dev headers which the
# macOS cross-compiler cannot use (different sysroot).
find_package(VulkanHeaders CONFIG QUIET)
if(VulkanHeaders_FOUND)
if(NOT TARGET Vulkan::Vulkan)
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
endif()
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Vulkan::Headers)
set(Vulkan_FOUND TRUE)
message(STATUS "Found Vulkan headers for macOS cross-compile via vcpkg VulkanHeaders")
endif()
else()
# Fallback: some distros / CMake versions need pkg-config to locate Vulkan.
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(VULKAN_PKG vulkan)
if(VULKAN_PKG_FOUND)
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
set_target_properties(Vulkan::Vulkan PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${VULKAN_PKG_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${VULKAN_PKG_LIBRARIES}"
)
if(VULKAN_PKG_LIBRARY_DIRS)
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
INTERFACE_LINK_DIRECTORIES "${VULKAN_PKG_LIBRARY_DIRS}")
endif()
set(Vulkan_FOUND TRUE)
message(STATUS "Found Vulkan via pkg-config: ${VULKAN_PKG_LIBRARIES}")
endif()
endif() endif()
endif() endif()
if(NOT Vulkan_FOUND) if(NOT Vulkan_FOUND)
message(FATAL_ERROR "Could not find Vulkan. Install libvulkan-dev (Linux), vulkan-loader (macOS), or the Vulkan SDK (Windows).") message(FATAL_ERROR "Could not find Vulkan. Install libvulkan-dev (Linux), vulkan-loader (macOS), or the Vulkan SDK (Windows).")
endif() endif()
endif() endif()
# macOS cross-compilation: the Vulkan loader (MoltenVK) is not available at link
# time. Allow unresolved Vulkan symbols — they are resolved at runtime.
if(CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_link_options("-undefined" "dynamic_lookup")
endif()
# GL/GLEW kept temporarily for unconverted sub-renderers during Vulkan migration. # GL/GLEW kept temporarily for unconverted sub-renderers during Vulkan migration.
# These files compile against GL types but their code is never called — the Vulkan # These files compile against GL types but their code is never called — the Vulkan
# path is the only active rendering backend. Remove in Phase 7 when all renderers # path is the only active rendering backend. Remove in Phase 7 when all renderers
@ -674,12 +744,16 @@ set(WOWEE_HEADERS
set(WOWEE_PLATFORM_SOURCES) set(WOWEE_PLATFORM_SOURCES)
if(WIN32) if(WIN32)
# Copy icon into build tree so llvm-rc can find it via the relative path in wowee.rc # Copy icon into build tree so windres can find it via the relative path
# in wowee.rc ("assets\\wowee.ico"). Tell the RC compiler to also search
# the build directory — GNU windres uses cwd (already the build dir) but
# llvm-windres resolves relative to the .rc file, so it needs the hint.
configure_file( configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/assets/Wowee.ico ${CMAKE_CURRENT_SOURCE_DIR}/assets/Wowee.ico
${CMAKE_CURRENT_BINARY_DIR}/assets/wowee.ico ${CMAKE_CURRENT_BINARY_DIR}/assets/wowee.ico
COPYONLY COPYONLY
) )
set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -I ${CMAKE_CURRENT_BINARY_DIR} -I ${CMAKE_CURRENT_SOURCE_DIR}")
list(APPEND WOWEE_PLATFORM_SOURCES resources/wowee.rc) list(APPEND WOWEE_PLATFORM_SOURCES resources/wowee.rc)
endif() endif()

283
container/FLOW.md Normal file
View file

@ -0,0 +1,283 @@
# Container Build Flow
Comprehensive documentation of the Docker-based build pipeline for each target platform.
---
## Architecture Overview
Each platform follows the same two-phase pattern:
1. **Image Build** (one-time, cached by Docker) — installs compilers, toolchains, and pre-builds vcpkg dependencies.
2. **Container Run** (each build) — copies source into the container, runs CMake configure + build, outputs artifacts to the host.
```
Host Docker
─────────────────────────────────────────────────────────────
run-{platform}.sh/.ps1
├─ docker build builder-{platform}.Dockerfile
│ (cached after first run) ├─ install compilers
│ ├─ install vcpkg + packages
│ └─ COPY build-{platform}.sh
└─ docker run build-{platform}.sh (entrypoint)
├─ bind /src (readonly) ├─ tar copy source → /wowee-build-src
└─ bind /out (writable) ├─ git clone FidelityFX SDKs
├─ cmake -S . -B /out
├─ cmake --build /out
└─ artifacts appear in /out
```
---
## Linux Build Flow
**Image:** `wowee-builder-linux`
**Dockerfile:** `builder-linux.Dockerfile`
**Toolchain:** GCC + Ninja (native amd64)
**Base:** Ubuntu 24.04
### Docker Image Build Steps
| Step | What | Why |
|------|------|-----|
| 1 | `apt-get install` cmake, ninja-build, build-essential, pkg-config, git, python3 | Core build tools |
| 2 | `apt-get install` glslang-tools, spirv-tools | Vulkan shader compilation |
| 3 | `apt-get install` libsdl2-dev, libglew-dev, libglm-dev, libssl-dev, zlib1g-dev | Runtime dependencies (system packages) |
| 4 | `apt-get install` libavformat-dev, libavcodec-dev, libswscale-dev, libavutil-dev | FFmpeg libraries |
| 5 | `apt-get install` libvulkan-dev, vulkan-tools | Vulkan SDK |
| 6 | `apt-get install` libstorm-dev, libunicorn-dev | MPQ archive + CPU emulation |
| 7 | COPY `build-linux.sh``/build-platform.sh` | Container entrypoint |
### Container Run Steps (build-linux.sh)
```
1. tar copy /src → /wowee-build-src (excludes build/, .git/, large Data/ dirs)
2. git clone FidelityFX-FSR2 (if missing)
3. git clone FidelityFX-SDK (if missing)
4. cmake configure:
-G Ninja
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_C_COMPILER=gcc
-DCMAKE_CXX_COMPILER=g++
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
5. cmake --build (parallel)
6. Create Data symlink: build/linux/bin/Data → ../../../Data
```
### Output
- `build/linux/bin/wowee` — ELF 64-bit x86-64 executable
- `build/linux/bin/Data` — symlink to project Data/ directory
---
## macOS Build Flow
**Image:** `wowee-builder-macos`
**Dockerfile:** `builder-macos.Dockerfile` (multi-stage)
**Toolchain:** osxcross (Clang 18 + Apple ld64)
**Base:** Ubuntu 24.04
**Targets:** arm64-apple-darwin24.5 (default), x86_64-apple-darwin24.5
### Docker Image Build — Stage 1: SDK Fetcher
The macOS SDK is fetched automatically from Apple's public software update catalog.
No manual download required.
| Step | What | Why |
|------|------|-----|
| 1 | `FROM ubuntu:24.04 AS sdk-fetcher` | Lightweight stage for SDK download |
| 2 | `apt-get install` ca-certificates, python3, cpio, tar, gzip, xz-utils | SDK extraction tools |
| 3 | COPY `macos/sdk-fetcher.py``/opt/sdk-fetcher.py` | Python script that scrapes Apple's SUCATALOG |
| 4 | `python3 /opt/sdk-fetcher.py /opt/sdk` | Downloads, extracts, and packages MacOSX15.5.sdk.tar.gz |
**SDK Fetcher internals** (`macos/sdk-fetcher.py`):
1. Queries Apple SUCATALOG URLs for the latest macOS package
2. Downloads the `CLTools_macOSNMOS_SDK.pkg` package
3. Extracts the XAR archive (using `bsdtar` or pure-Python fallback)
4. Decompresses the PBZX payload stream
5. Extracts via `cpio` to get the SDK directory
6. Packages as `MacOSX<version>.sdk.tar.gz`
### Docker Image Build — Stage 2: Builder
| Step | What | Why |
|------|------|-----|
| 1 | `FROM ubuntu:24.04 AS builder` | Full build environment |
| 2 | `apt-get install` cmake, ninja-build, git, python3, curl, wget, xz-utils, zip, unzip, tar, make, patch, libssl-dev, zlib1g-dev, pkg-config, libbz2-dev, libxml2-dev, uuid-dev | Build tools + osxcross build deps |
| 3 | Install Clang 18 from LLVM apt repo (`llvm-toolchain-jammy-18`) | Cross-compiler backend |
| 4 | Symlink clang-18 → clang, clang++-18 → clang++, etc. | osxcross expects unversioned names |
| 5 | `git clone osxcross``/opt/osxcross` | Apple cross-compile toolchain wrapper |
| 6 | `COPY --from=sdk-fetcher /opt/sdk/ → /opt/osxcross/tarballs/` | SDK from stage 1 |
| 7 | `UNATTENDED=1 ./build.sh` | Builds osxcross (LLVM wrappers + cctools + ld64) |
| 8 | Create unprefixed symlinks (install_name_tool, otool, lipo, codesign) | vcpkg/CMake need these without arch prefix |
| 9 | COPY `macos/osxcross-toolchain.cmake``/opt/osxcross-toolchain.cmake` | Auto-detecting CMake toolchain |
| 10 | COPY `macos/triplets/``/opt/vcpkg-triplets/` | vcpkg cross-compile triplet definitions |
| 11 | `apt-get install` file, nasm | Mach-O detection + ffmpeg x86 asm |
| 12 | Bootstrap vcpkg → `/opt/vcpkg` | Package manager |
| 13 | `vcpkg install` sdl2, openssl, glew, glm, zlib, ffmpeg `--triplet arm64-osx-cross` | arm64 dependencies |
| 14 | `vcpkg install` same packages `--triplet x64-osx-cross` | x86_64 dependencies |
| 15 | `apt-get install` libvulkan-dev, glslang-tools | Vulkan headers (for compilation, not runtime) |
| 16 | COPY `build-macos.sh``/build-platform.sh` | Container entrypoint |
### Custom Toolchain Files
**`macos/osxcross-toolchain.cmake`** — Auto-detecting CMake toolchain:
- Detects SDK path via `file(GLOB)` in `/opt/osxcross/target/SDK/MacOSX*.sdk`
- Detects darwin version from compiler binary names (e.g., `arm64-apple-darwin24.5-clang`)
- Picks architecture from `CMAKE_OSX_ARCHITECTURES`
- Sets `CMAKE_C_COMPILER`, `CMAKE_CXX_COMPILER`, `CMAKE_AR`, `CMAKE_RANLIB`, `CMAKE_STRIP`
**`macos/triplets/arm64-osx-cross.cmake`**:
```cmake
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE /opt/osxcross-toolchain.cmake)
```
### Container Run Steps (build-macos.sh)
```
1. Determine arch from MACOS_ARCH env (default: arm64)
2. Pick vcpkg triplet: arm64-osx-cross or x64-osx-cross
3. Auto-detect darwin target from osxcross binaries
4. tar copy /src → /wowee-build-src
5. git clone FidelityFX-FSR2 + FidelityFX-SDK (if missing)
6. cmake configure:
-G Ninja
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_SYSTEM_NAME=Darwin
-DCMAKE_OSX_ARCHITECTURES=${ARCH}
-DCMAKE_C_COMPILER=osxcross clang
-DCMAKE_CXX_COMPILER=osxcross clang++
-DCMAKE_TOOLCHAIN_FILE=vcpkg.cmake
-DVCPKG_TARGET_TRIPLET=arm64-osx-cross
-DVCPKG_OVERLAY_TRIPLETS=/opt/vcpkg-triplets
7. cmake --build (parallel)
```
### CMakeLists.txt Integration
The main CMakeLists.txt has a macOS cross-compile branch that:
- Finds Vulkan headers via vcpkg (`VulkanHeaders` package) instead of the Vulkan SDK
- Adds `-undefined dynamic_lookup` linker flag for Vulkan loader symbols (resolved at runtime via MoltenVK)
### Output
- `build/macos/bin/wowee` — Mach-O 64-bit arm64 (or x86_64) executable (~40 MB)
---
## Windows Build Flow
**Image:** `wowee-builder-windows`
**Dockerfile:** `builder-windows.Dockerfile`
**Toolchain:** LLVM-MinGW (Clang + LLD) targeting x86_64-w64-mingw32-ucrt
**Base:** Ubuntu 24.04
### Docker Image Build Steps
| Step | What | Why |
|------|------|-----|
| 1 | `apt-get install` ca-certificates, build-essential, cmake, ninja-build, git, python3, curl, zip, unzip, tar, xz-utils, pkg-config, nasm, libssl-dev, zlib1g-dev | Build tools |
| 2 | Download + extract LLVM-MinGW (v20240619 ucrt) → `/opt/llvm-mingw` | Clang/LLD cross-compiler for Windows |
| 3 | Add `/opt/llvm-mingw/bin` to PATH | Makes `x86_64-w64-mingw32-clang` etc. available |
| 4 | Bootstrap vcpkg → `/opt/vcpkg` | Package manager |
| 5 | `vcpkg install` sdl2, openssl, glew, glm, zlib, ffmpeg `--triplet x64-mingw-static` | Static Windows dependencies |
| 6 | `apt-get install` libvulkan-dev, glslang-tools | Vulkan headers + shader tools |
| 7 | Create no-op `powershell.exe` stub | vcpkg MinGW post-build hook needs it |
| 8 | COPY `build-windows.sh``/build-platform.sh` | Container entrypoint |
### Container Run Steps (build-windows.sh)
```
1. Set up no-op powershell.exe (if not already present)
2. tar copy /src → /wowee-build-src
3. git clone FidelityFX-FSR2 + FidelityFX-SDK (if missing)
4. Generate Vulkan import library:
a. Extract vk* symbols from vulkan_core.h
b. Create vulkan-1.def file
c. Run dlltool to create libvulkan-1.a
5. Lock PKG_CONFIG_LIBDIR to vcpkg packages only
6. cmake configure:
-G Ninja
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_SYSTEM_NAME=Windows
-DCMAKE_C_COMPILER=x86_64-w64-mingw32-clang
-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-clang++
-DCMAKE_RC_COMPILER=x86_64-w64-mingw32-windres
-DCMAKE_EXE_LINKER_FLAGS=-fuse-ld=lld
-DCMAKE_TOOLCHAIN_FILE=vcpkg.cmake
-DVCPKG_TARGET_TRIPLET=x64-mingw-static
-DVCPKG_APPLOCAL_DEPS=OFF
7. cmake --build (parallel)
```
### Vulkan Import Library Generation
Windows applications link against `vulkan-1.dll` (the Khronos Vulkan loader). Since the LLVM-MinGW toolchain doesn't ship a Vulkan import library, the build script generates one:
1. Parses `vulkan_core.h` for `VKAPI_CALL vk*` function names
2. Creates a `.def` file mapping symbols to `vulkan-1.dll`
3. Uses `dlltool` to produce `libvulkan-1.a` (PE import library)
This allows the linker to resolve Vulkan symbols at build time, while deferring actual loading to the runtime DLL.
### Output
- `build/windows/bin/wowee.exe` — PE32+ x86-64 executable (~135 MB)
---
## Shared Patterns
### Source Tree Copy
All three platforms use the same tar-based copy with exclusions:
```bash
tar -C /src \
--exclude='./build' --exclude='./logs' --exclude='./cache' \
--exclude='./container' --exclude='./.git' \
--exclude='./Data/character' --exclude='./Data/creature' \
--exclude='./Data/db' --exclude='./Data/environment' \
--exclude='./Data/interface' --exclude='./Data/item' \
--exclude='./Data/misc' --exclude='./Data/sound' \
--exclude='./Data/spell' --exclude='./Data/terrain' \
--exclude='./Data/world' \
-cf - . | tar -C /wowee-build-src -xf -
```
**Kept:** `Data/opcodes/`, `Data/expansions/` (small, needed at build time for configuration).
**Excluded:** Large game asset directories (character, creature, environment, etc.) not needed for compilation.
### FidelityFX SDK Fetch
All platforms clone the same two repos at build time:
1. **FidelityFX-FSR2** — FSR 2.0 upscaling
2. **FidelityFX-SDK** — FSR 3.0 frame generation (repo URL/ref configurable via env vars)
### .dockerignore
The `.dockerignore` at the project root minimizes the Docker build context by excluding:
- `build/`, `cache/`, `logs/`, `.git/`
- Large external dirs (`extern/FidelityFX-*`)
- IDE files, documentation, host-only scripts
- SDK tarballs (`*.tar.xz`, `*.tar.gz`, etc.)
---
## Timing Estimates
These are approximate times on a 4-core machine with 16 GB RAM:
| Phase | Linux | macOS | Windows |
|-------|-------|-------|---------|
| Docker image build (first time) | ~5 min | ~25 min | ~15 min |
| Docker image build (cached) | seconds | seconds | seconds |
| Source copy + SDK fetch | ~10 sec | ~10 sec | ~10 sec |
| CMake configure | ~20 sec | ~30 sec | ~30 sec |
| Compilation | ~8 min | ~8 min | ~8 min |
| **Total (first build)** | **~14 min** | **~34 min** | **~24 min** |
| **Total (subsequent)** | **~9 min** | **~9 min** | **~9 min** |
macOS image is slowest because osxcross builds a subset of LLVM + cctools, and vcpkg packages are compiled for two architectures (arm64 + x64).

119
container/README.md Normal file
View file

@ -0,0 +1,119 @@
# Container Builds
Build WoWee for **Linux**, **macOS**, or **Windows** with a single command.
All builds run inside Docker — no toolchains to install on your host.
## Prerequisites
- [Docker](https://docs.docker.com/get-docker/) (Docker Desktop on Windows/macOS, or Docker Engine on Linux)
- ~20 GB free disk space (toolchains + vcpkg packages are cached in the Docker image)
## Quick Start
Run **from the project root directory**.
### Linux (native amd64)
```bash
# Bash / Linux / macOS terminal
./container/run-linux.sh
```
```powershell
# PowerShell (Windows)
.\container\run-linux.ps1
```
Output: `build/linux/bin/wowee`
### macOS (cross-compile, arm64 default)
```bash
./container/run-macos.sh
```
```powershell
.\container\run-macos.ps1
```
Output: `build/macos/bin/wowee`
For Intel (x86_64):
```bash
MACOS_ARCH=x86_64 ./container/run-macos.sh
```
```powershell
.\container\run-macos.ps1 -Arch x86_64
```
### Windows (cross-compile, x86_64)
```bash
./container/run-windows.sh
```
```powershell
.\container\run-windows.ps1
```
Output: `build/windows/bin/wowee.exe`
## Options
| Option | Bash | PowerShell | Description |
|--------|------|------------|-------------|
| Rebuild image | `--rebuild-image` | `-RebuildImage` | Force a fresh Docker image build |
| macOS arch | `MACOS_ARCH=x86_64` | `-Arch x86_64` | Build for Intel instead of Apple Silicon |
| FidelityFX SDK repo | `WOWEE_FFX_SDK_REPO=<url>` | `$env:WOWEE_FFX_SDK_REPO="<url>"` | Custom FidelityFX SDK git URL |
| FidelityFX SDK ref | `WOWEE_FFX_SDK_REF=<ref>` | `$env:WOWEE_FFX_SDK_REF="<ref>"` | Custom FidelityFX SDK git ref/tag |
## Docker Image Caching
The first build takes longer because Docker builds the toolchain image (installing compilers, vcpkg packages, etc.). Subsequent builds reuse the cached image and only run the compilation step.
To force a full image rebuild:
```bash
./container/run-linux.sh --rebuild-image
```
## Output Locations
| Target | Binary | Size |
|--------|--------|------|
| Linux | `build/linux/bin/wowee` | ~135 MB |
| macOS | `build/macos/bin/wowee` | ~40 MB |
| Windows | `build/windows/bin/wowee.exe` | ~135 MB |
## File Structure
```
container/
├── run-linux.sh / .ps1 # Host launchers (bash / PowerShell)
├── run-macos.sh / .ps1
├── run-windows.sh / .ps1
├── build-linux.sh # Container entrypoints (run inside Docker)
├── build-macos.sh
├── build-windows.sh
├── builder-linux.Dockerfile # Docker image definitions
├── builder-macos.Dockerfile
├── builder-windows.Dockerfile
├── macos/
│ ├── sdk-fetcher.py # Auto-fetches macOS SDK from Apple's catalog
│ ├── osxcross-toolchain.cmake # CMake toolchain for osxcross
│ └── triplets/ # vcpkg cross-compile triplets
│ ├── arm64-osx-cross.cmake
│ └── x64-osx-cross.cmake
├── README.md # This file
└── FLOW.md # Detailed build flow documentation
```
## Troubleshooting
**"docker is not installed or not in PATH"**
Install Docker and ensure the `docker` command is available in your terminal.
**Build fails on first run**
Some vcpkg packages (ffmpeg, SDL2) take a while to compile. Ensure you have enough RAM (4 GB+) and disk space.
**macOS build: "could not find osxcross compiler"**
The Docker image may not have built correctly. Run with `--rebuild-image` to rebuild from scratch.
**Windows build: linker errors about vulkan-1.dll**
The build script auto-generates a Vulkan import library. If this fails, ensure the Docker image has `libvulkan-dev` installed (it should, by default).

View file

@ -1,19 +0,0 @@
#!/bin/bash
set -eu
set -o pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
podman build \
-f "${SCRIPT_DIR}/builder-ubuntu.Dockerfile" \
-t wowee-builder-ubuntu
BUILD_DIR="$(mktemp --tmpdir -d wowee.XXXXX \
--suffix=".$(cd "${PROJECT_ROOT}"; git rev-parse --short HEAD)")"
podman run \
--mount "type=bind,src=${PROJECT_ROOT},dst=/WoWee-src,ro=true" \
--mount "type=bind,src=${BUILD_DIR},dst=/build" \
localhost/wowee-builder-ubuntu \
./build-wowee.sh

62
container/build-linux.sh Executable file
View file

@ -0,0 +1,62 @@
#!/bin/bash
# Linux amd64 build entrypoint — runs INSIDE the linux container.
# Bind-mounts:
# /src (ro) — project source
# /out (rw) — host ./build/linux
set -euo pipefail
SRC=/src
OUT=/out
NPROC=$(nproc)
echo "==> [linux] Copying source tree..."
tar -C "${SRC}" \
--exclude='./build' --exclude='./logs' --exclude='./cache' \
--exclude='./container' --exclude='./.git' \
--exclude='./Data/character' --exclude='./Data/creature' \
--exclude='./Data/db' --exclude='./Data/environment' \
--exclude='./Data/interface' --exclude='./Data/item' \
--exclude='./Data/misc' --exclude='./Data/sound' \
--exclude='./Data/spell' --exclude='./Data/terrain' \
--exclude='./Data/world' \
-cf - . | tar -C /wowee-build-src -xf -
cd /wowee-build-src
echo "==> [linux] Fetching external SDKs (if needed)..."
if [ ! -f extern/FidelityFX-FSR2/src/ffx-fsr2-api/ffx_fsr2.h ]; then
git clone --depth 1 \
https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git \
extern/FidelityFX-FSR2 || echo "Warning: FSR2 clone failed — continuing without FSR2"
fi
SDK_REPO="${WOWEE_FFX_SDK_REPO:-https://github.com/Kelsidavis/FidelityFX-SDK.git}"
SDK_REF="${WOWEE_FFX_SDK_REF:-main}"
if [ ! -f "extern/FidelityFX-SDK/sdk/include/FidelityFX/host/ffx_frameinterpolation.h" ]; then
git clone --depth 1 --branch "${SDK_REF}" "${SDK_REPO}" extern/FidelityFX-SDK \
|| echo "Warning: FidelityFX-SDK clone failed — continuing without FSR3"
fi
echo "==> [linux] Configuring with CMake (Release, Ninja, amd64)..."
cmake -S . -B "${OUT}" \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=gcc \
-DCMAKE_CXX_COMPILER=g++ \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
echo "==> [linux] Building with ${NPROC} cores..."
cmake --build "${OUT}" --parallel "${NPROC}"
echo "==> [linux] Creating Data symlink..."
mkdir -p "${OUT}/bin"
if [ ! -e "${OUT}/bin/Data" ]; then
# Relative symlink so it resolves correctly on the host:
# build/linux/bin/Data -> ../../../Data (project root)
ln -s ../../../Data "${OUT}/bin/Data"
fi
echo ""
echo "==> [linux] Build complete. Artifacts in: ./build/linux/"
echo " Binary: ./build/linux/bin/wowee"

83
container/build-macos.sh Executable file
View file

@ -0,0 +1,83 @@
#!/bin/bash
# macOS cross-compile entrypoint — runs INSIDE the macos container.
# Toolchain: osxcross + Apple Clang, target: arm64-apple-darwin (default) or
# x86_64-apple-darwin when MACOS_ARCH=x86_64.
# Bind-mounts:
# /src (ro) — project source
# /out (rw) — host ./build/macos
set -euo pipefail
SRC=/src
OUT=/out
NPROC=$(nproc)
# Arch selection: arm64 (Apple Silicon) is the default primary target.
ARCH="${MACOS_ARCH:-arm64}"
case "${ARCH}" in
arm64) VCPKG_TRIPLET=arm64-osx-cross ;;
x86_64) VCPKG_TRIPLET=x64-osx-cross ;;
*) echo "ERROR: unsupported MACOS_ARCH '${ARCH}'. Use arm64 or x86_64." ; exit 1 ;;
esac
# Auto-detect darwin target from osxcross binaries (e.g. arm64-apple-darwin24.5).
OSXCROSS_BIN=/opt/osxcross/target/bin
TARGET=$(basename "$(ls "${OSXCROSS_BIN}/${ARCH}-apple-darwin"*-clang 2>/dev/null | head -1)" | sed 's/-clang$//')
if [[ -z "${TARGET}" ]]; then
echo "ERROR: could not find osxcross ${ARCH} compiler in ${OSXCROSS_BIN}" >&2
exit 1
fi
echo "==> Detected osxcross target: ${TARGET}"
echo "==> [macos/${ARCH}] Copying source tree..."
mkdir -p /wowee-build-src
tar -C "${SRC}" \
--exclude='./build' --exclude='./logs' --exclude='./cache' \
--exclude='./container' --exclude='./.git' \
--exclude='./Data/character' --exclude='./Data/creature' \
--exclude='./Data/db' --exclude='./Data/environment' \
--exclude='./Data/interface' --exclude='./Data/item' \
--exclude='./Data/misc' --exclude='./Data/sound' \
--exclude='./Data/spell' --exclude='./Data/terrain' \
--exclude='./Data/world' \
-cf - . | tar -C /wowee-build-src -xf -
cd /wowee-build-src
echo "==> [macos/${ARCH}] Fetching external SDKs (if needed)..."
if [ ! -f extern/FidelityFX-FSR2/src/ffx-fsr2-api/ffx_fsr2.h ]; then
git clone --depth 1 \
https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git \
extern/FidelityFX-FSR2 || echo "Warning: FSR2 clone failed"
fi
SDK_REPO="${WOWEE_FFX_SDK_REPO:-https://github.com/Kelsidavis/FidelityFX-SDK.git}"
SDK_REF="${WOWEE_FFX_SDK_REF:-main}"
if [ ! -f "extern/FidelityFX-SDK/sdk/include/FidelityFX/host/ffx_frameinterpolation.h" ]; then
git clone --depth 1 --branch "${SDK_REF}" "${SDK_REPO}" extern/FidelityFX-SDK \
|| echo "Warning: FidelityFX-SDK clone failed"
fi
echo "==> [macos/${ARCH}] Configuring with CMake (Release, Ninja, osxcross ${TARGET})..."
cmake -S . -B "${OUT}" \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_SYSTEM_NAME=Darwin \
-DCMAKE_OSX_ARCHITECTURES="${ARCH}" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-13.0}" \
-DCMAKE_C_COMPILER="${OSXCROSS_BIN}/${TARGET}-clang" \
-DCMAKE_CXX_COMPILER="${OSXCROSS_BIN}/${TARGET}-clang++" \
-DCMAKE_AR="${OSXCROSS_BIN}/${TARGET}-ar" \
-DCMAKE_RANLIB="${OSXCROSS_BIN}/${TARGET}-ranlib" \
-DCMAKE_TOOLCHAIN_FILE="${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_TARGET_TRIPLET="${VCPKG_TRIPLET}" \
-DVCPKG_OVERLAY_TRIPLETS=/opt/vcpkg-triplets \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF \
-DWOWEE_ENABLE_ASAN=OFF
echo "==> [macos/${ARCH}] Building with ${NPROC} cores..."
cmake --build "${OUT}" --parallel "${NPROC}"
echo ""
echo "==> [macos/${ARCH}] Build complete. Artifacts in: ./build/macos/"
echo " Binary: ./build/macos/bin/wowee"

110
container/build-windows.sh Executable file
View file

@ -0,0 +1,110 @@
#!/bin/bash
# Windows cross-compile entrypoint — runs INSIDE the windows container.
# Toolchain: LLVM-MinGW (Clang + LLD), target: x86_64-w64-mingw32-ucrt
# Bind-mounts:
# /src (ro) — project source
# /out (rw) — host ./build/windows
set -euo pipefail
SRC=/src
OUT=/out
NPROC=$(nproc)
TARGET=x86_64-w64-mingw32
# vcpkg's MinGW applocal hook always appends a powershell.exe post-build step to
# copy DLLs next to each binary, even when VCPKG_APPLOCAL_DEPS=OFF. For the
# x64-mingw-static triplet the bin/ dir is empty (no DLLs) so the script does
# nothing — but it still needs to exit 0. Provide a no-op stub if the real
# PowerShell isn't available.
if ! command -v powershell.exe &>/dev/null; then
printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/powershell.exe
chmod +x /usr/local/bin/powershell.exe
fi
echo "==> [windows] Copying source tree..."
mkdir -p /wowee-build-src
tar -C "${SRC}" \
--exclude='./build' --exclude='./logs' --exclude='./cache' \
--exclude='./container' --exclude='./.git' \
--exclude='./Data/character' --exclude='./Data/creature' \
--exclude='./Data/db' --exclude='./Data/environment' \
--exclude='./Data/interface' --exclude='./Data/item' \
--exclude='./Data/misc' --exclude='./Data/sound' \
--exclude='./Data/spell' --exclude='./Data/terrain' \
--exclude='./Data/world' \
-cf - . | tar -C /wowee-build-src -xf -
cd /wowee-build-src
echo "==> [windows] Fetching external SDKs (if needed)..."
if [ ! -f extern/FidelityFX-FSR2/src/ffx-fsr2-api/ffx_fsr2.h ]; then
git clone --depth 1 \
https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git \
extern/FidelityFX-FSR2 || echo "Warning: FSR2 clone failed"
fi
SDK_REPO="${WOWEE_FFX_SDK_REPO:-https://github.com/Kelsidavis/FidelityFX-SDK.git}"
SDK_REF="${WOWEE_FFX_SDK_REF:-main}"
if [ ! -f "extern/FidelityFX-SDK/sdk/include/FidelityFX/host/ffx_frameinterpolation.h" ]; then
git clone --depth 1 --branch "${SDK_REF}" "${SDK_REPO}" extern/FidelityFX-SDK \
|| echo "Warning: FidelityFX-SDK clone failed"
fi
echo "==> [windows] Generating Vulkan import library for cross-compile..."
# Windows applications link against vulkan-1.dll (the Khronos Vulkan loader).
# The cross-compile toolchain only ships Vulkan *headers* (via vcpkg), not the
# import library. Generate a minimal libvulkan-1.a from the header prototypes
# so the linker can resolve vk* symbols → vulkan-1.dll at runtime.
# We use the host libvulkan-dev header for function name extraction — the Vulkan
# API prototypes are platform-independent.
VULKAN_IMP_DIR="${OUT}/vulkan-import"
if [ ! -f "${VULKAN_IMP_DIR}/libvulkan-1.a" ]; then
mkdir -p "${VULKAN_IMP_DIR}"
# Try vcpkg-installed header first (available on incremental builds),
# then fall back to the host libvulkan-dev header (always present in the image).
VK_HEADER="${OUT}/vcpkg_installed/x64-mingw-static/include/vulkan/vulkan_core.h"
if [ ! -f "${VK_HEADER}" ]; then
VK_HEADER="/usr/include/vulkan/vulkan_core.h"
fi
{
echo "LIBRARY vulkan-1.dll"
echo "EXPORTS"
grep -oP 'VKAPI_ATTR \S+ VKAPI_CALL \K(vk\w+)' "${VK_HEADER}" | sort -u | sed 's/^/ /'
} > "${VULKAN_IMP_DIR}/vulkan-1.def"
"${TARGET}-dlltool" -d "${VULKAN_IMP_DIR}/vulkan-1.def" \
-l "${VULKAN_IMP_DIR}/libvulkan-1.a" -m i386:x86-64
echo " Generated $(wc -l < "${VULKAN_IMP_DIR}/vulkan-1.def") export entries"
fi
echo "==> [windows] Configuring with CMake (Release, Ninja, LLVM-MinGW cross)..."
# Lock pkg-config to the cross-compiled vcpkg packages only.
# Without this, CMake's Vulkan pkg-config fallback finds the *Linux* libvulkan-dev
# and injects /usr/include into every MinGW compile command, which then fails
# because the glibc-specific bits/libc-header-start.h is not in the MinGW sysroot.
export PKG_CONFIG_LIBDIR="${OUT}/vcpkg_installed/x64-mingw-static/lib/pkgconfig"
export PKG_CONFIG_PATH=""
cmake -S . -B "${OUT}" \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_C_COMPILER="${TARGET}-clang" \
-DCMAKE_CXX_COMPILER="${TARGET}-clang++" \
-DCMAKE_RC_COMPILER="${TARGET}-windres" \
-DCMAKE_AR="/opt/llvm-mingw/bin/llvm-ar" \
-DCMAKE_RANLIB="/opt/llvm-mingw/bin/llvm-ranlib" \
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \
-DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld" \
-DCMAKE_TOOLCHAIN_FILE="${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_TARGET_TRIPLET=x64-mingw-static \
-DVCPKG_APPLOCAL_DEPS=OFF \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF \
-DWOWEE_ENABLE_ASAN=OFF
echo "==> [windows] Building with ${NPROC} cores..."
cmake --build "${OUT}" --parallel "${NPROC}"
echo ""
echo "==> [windows] Build complete. Artifacts in: ./build/windows/"
echo " Binary: ./build/windows/bin/wowee.exe"

View file

@ -1,14 +0,0 @@
#!/bin/bash
set -eu
set -o pipefail
cp -r /WoWee-src /WoWee
pushd /WoWee
./build.sh
popd
pushd /WoWee/build
cmake --install . --prefix=/build
popd

View file

@ -0,0 +1,33 @@
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends \
cmake \
ninja-build \
build-essential \
pkg-config \
git \
python3 \
glslang-tools \
spirv-tools \
libsdl2-dev \
libglew-dev \
libglm-dev \
libssl-dev \
zlib1g-dev \
libavformat-dev \
libavcodec-dev \
libswscale-dev \
libavutil-dev \
libvulkan-dev \
vulkan-tools \
libstorm-dev \
libunicorn-dev && \
rm -rf /var/lib/apt/lists/*
COPY build-linux.sh /build-platform.sh
RUN chmod +x /build-platform.sh
ENTRYPOINT ["/build-platform.sh"]

View file

@ -0,0 +1,143 @@
FROM ubuntu:24.04 AS sdk-fetcher
# Stage 1: Fetch macOS SDK from Apple's public software update catalog.
# This avoids requiring the user to supply the SDK tarball manually.
# The SDK is downloaded, extracted, and packaged as a .tar.gz.
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
python3 \
cpio \
tar \
gzip \
xz-utils && \
rm -rf /var/lib/apt/lists/*
COPY macos/sdk-fetcher.py /opt/sdk-fetcher.py
RUN python3 /opt/sdk-fetcher.py /opt/sdk
# ---------------------------------------------------------------------------
FROM ubuntu:24.04 AS builder
# Stage 2: macOS cross-compile image using osxcross + Clang 18.
#
# Target triplets (auto-detected from osxcross):
# arm64-apple-darwinNN (Apple Silicon)
# x86_64-apple-darwinNN (Intel)
# Default: arm64. Override with MACOS_ARCH=x86_64 env var at run time.
ENV DEBIAN_FRONTEND=noninteractive
ENV OSXCROSS_VERSION=1.5
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
cmake \
ninja-build \
git \
python3 \
curl \
wget \
xz-utils \
zip \
unzip \
tar \
make \
patch \
libssl-dev \
zlib1g-dev \
pkg-config \
libbz2-dev \
libxml2-dev \
libz-dev \
liblzma-dev \
uuid-dev \
python3-lxml \
gnupg \
software-properties-common && \
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" > /etc/apt/sources.list.d/llvm-18.list && \
apt-get update && \
apt-get install -y --no-install-recommends \
clang-18 \
lld-18 \
llvm-18 && \
ln -sf /usr/bin/clang-18 /usr/bin/clang && \
ln -sf /usr/bin/clang++-18 /usr/bin/clang++ && \
ln -sf /usr/bin/lld-18 /usr/bin/lld && \
ln -sf /usr/bin/ld.lld-18 /usr/bin/ld.lld && \
ln -sf /usr/bin/llvm-ar-18 /usr/bin/llvm-ar && \
rm -rf /var/lib/apt/lists/*
# Build osxcross with SDK from stage 1
RUN git clone --depth 1 https://github.com/tpoechtrager/osxcross.git /opt/osxcross
COPY --from=sdk-fetcher /opt/sdk/ /opt/osxcross/tarballs/
ENV MACOSX_DEPLOYMENT_TARGET=13.0
RUN cd /opt/osxcross && \
unset OSXCROSS_VERSION && \
UNATTENDED=1 ./build.sh && \
rm -rf /opt/osxcross/build /opt/osxcross/tarballs
ENV PATH="/opt/osxcross/target/bin:${PATH}"
ENV OSXCROSS_TARGET_DIR="/opt/osxcross/target"
ENV MACOSX_DEPLOYMENT_TARGET=13.0
# Create unprefixed symlinks for macOS tools that vcpkg/CMake expect
RUN cd /opt/osxcross/target/bin && \
for tool in install_name_tool otool lipo codesign; do \
src="$(ls *-apple-darwin*-"${tool}" 2>/dev/null | head -1)"; \
if [ -n "$src" ]; then \
ln -sf "$src" "$tool"; \
fi; \
done
# Custom osxcross toolchain + vcpkg triplets
COPY macos/osxcross-toolchain.cmake /opt/osxcross-toolchain.cmake
COPY macos/triplets/ /opt/vcpkg-triplets/
# Extra tools needed by vcpkg's Mach-O rpath fixup and ffmpeg x86 asm
RUN apt-get update && \
apt-get install -y --no-install-recommends file nasm && \
rm -rf /var/lib/apt/lists/*
# vcpkg — macOS cross triplets (arm64-osx-cross / x64-osx-cross)
ENV VCPKG_ROOT=/opt/vcpkg
RUN git clone --depth 1 https://github.com/microsoft/vcpkg.git "${VCPKG_ROOT}" && \
"${VCPKG_ROOT}/bootstrap-vcpkg.sh" -disableMetrics
# Pre-install deps for both arches; the launcher script picks the right one at run time.
RUN "${VCPKG_ROOT}/vcpkg" install \
sdl2[vulkan] \
openssl \
glew \
glm \
zlib \
ffmpeg \
--triplet arm64-osx-cross \
--overlay-triplets=/opt/vcpkg-triplets
RUN "${VCPKG_ROOT}/vcpkg" install \
sdl2[vulkan] \
openssl \
glew \
glm \
zlib \
ffmpeg \
--triplet x64-osx-cross \
--overlay-triplets=/opt/vcpkg-triplets
# Vulkan SDK headers (MoltenVK is the runtime — headers only needed to compile)
RUN apt-get update && \
apt-get install -y --no-install-recommends libvulkan-dev glslang-tools && \
rm -rf /var/lib/apt/lists/*
COPY build-macos.sh /build-platform.sh
RUN chmod +x /build-platform.sh
ENTRYPOINT ["/build-platform.sh"]

View file

@ -1,25 +0,0 @@
FROM ubuntu:24.04
RUN apt-get update && \
apt install -y \
cmake \
build-essential \
pkg-config \
git \
libsdl2-dev \
libglew-dev \
libglm-dev \
libssl-dev \
zlib1g-dev \
libavformat-dev \
libavcodec-dev \
libswscale-dev \
libavutil-dev \
libvulkan-dev \
vulkan-tools \
libstorm-dev && \
rm -rf /var/lib/apt/lists/*
COPY build-wowee.sh /
ENTRYPOINT ./build-wowee.sh

View file

@ -0,0 +1,67 @@
FROM ubuntu:24.04
# Windows cross-compile using LLVM-MinGW — best-in-class Clang/LLD toolchain
# targeting x86_64-w64-mingw32. Produces native .exe/.dll without MSVC or Wine.
# LLVM-MinGW ships: clang, clang++, lld, libc++ / libunwind headers, winpthreads.
ENV DEBIAN_FRONTEND=noninteractive
ENV LLVM_MINGW_VERSION=20240619
ENV LLVM_MINGW_URL=https://github.com/mstorsjo/llvm-mingw/releases/download/${LLVM_MINGW_VERSION}/llvm-mingw-${LLVM_MINGW_VERSION}-ucrt-ubuntu-20.04-x86_64.tar.xz
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
build-essential \
cmake \
ninja-build \
git \
python3 \
curl \
zip \
unzip \
tar \
xz-utils \
pkg-config \
nasm \
libssl-dev \
zlib1g-dev && \
rm -rf /var/lib/apt/lists/*
# Install LLVM-MinGW toolchain
RUN curl -fsSL "${LLVM_MINGW_URL}" -o /tmp/llvm-mingw.tar.xz && \
tar -xf /tmp/llvm-mingw.tar.xz -C /opt && \
mv /opt/llvm-mingw-${LLVM_MINGW_VERSION}-ucrt-ubuntu-20.04-x86_64 /opt/llvm-mingw && \
rm /tmp/llvm-mingw.tar.xz
ENV PATH="/opt/llvm-mingw/bin:${PATH}"
# Windows dependencies via vcpkg (static, x64-mingw-static triplet)
ENV VCPKG_ROOT=/opt/vcpkg
RUN git clone --depth 1 https://github.com/microsoft/vcpkg.git "${VCPKG_ROOT}" && \
"${VCPKG_ROOT}/bootstrap-vcpkg.sh" -disableMetrics
ENV VCPKG_DEFAULT_TRIPLET=x64-mingw-static
RUN "${VCPKG_ROOT}/vcpkg" install \
sdl2[vulkan] \
openssl \
glew \
glm \
zlib \
ffmpeg \
--triplet x64-mingw-static
# Vulkan SDK headers (loader is linked statically via SDL2's vulkan surface)
RUN apt-get update && \
apt-get install -y --no-install-recommends libvulkan-dev glslang-tools && \
rm -rf /var/lib/apt/lists/*
# Provide a no-op powershell.exe so vcpkg's MinGW applocal post-build hook
# exits cleanly. The x64-mingw-static triplet is fully static (no DLLs to
# copy), so the script has nothing to do — it just needs to not fail.
RUN printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/powershell.exe && \
chmod +x /usr/local/bin/powershell.exe
COPY build-windows.sh /build-platform.sh
RUN chmod +x /build-platform.sh
ENTRYPOINT ["/build-platform.sh"]

View file

@ -0,0 +1,62 @@
# osxcross CMake toolchain file for cross-compiling to macOS from Linux.
# Used by vcpkg triplets and the WoWee build.
# Auto-detects SDK, darwin version, and arch from the osxcross installation
# and the VCPKG_OSX_ARCHITECTURES / CMAKE_OSX_ARCHITECTURES setting.
set(CMAKE_SYSTEM_NAME Darwin)
# osxcross paths
set(_target_dir "/opt/osxcross/target")
if(DEFINED ENV{OSXCROSS_TARGET_DIR})
set(_target_dir "$ENV{OSXCROSS_TARGET_DIR}")
endif()
# Auto-detect SDK (pick the newest if several are present)
file(GLOB _sdk_dirs "${_target_dir}/SDK/MacOSX*.sdk")
list(SORT _sdk_dirs)
list(GET _sdk_dirs -1 _sdk_dir)
set(CMAKE_OSX_SYSROOT "${_sdk_dir}" CACHE PATH "" FORCE)
# Deployment target
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "" FORCE)
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
set(CMAKE_OSX_DEPLOYMENT_TARGET "$ENV{MACOSX_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
endif()
# auto-detect darwin version from compiler names
file(GLOB _darwin_compilers "${_target_dir}/bin/*-apple-darwin*-clang")
list(GET _darwin_compilers 0 _first_compiler)
get_filename_component(_compiler_name "${_first_compiler}" NAME)
string(REGEX MATCH "apple-darwin[0-9.]+" _darwin_part "${_compiler_name}")
# pick architecture
# CMAKE_OSX_ARCHITECTURES is set by vcpkg from VCPKG_OSX_ARCHITECTURES
if(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
set(_arch "arm64")
elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64")
set(_arch "x86_64")
elseif(DEFINED ENV{OSXCROSS_ARCH})
set(_arch "$ENV{OSXCROSS_ARCH}")
else()
set(_arch "arm64")
endif()
set(_host "${_arch}-${_darwin_part}")
set(CMAKE_SYSTEM_PROCESSOR "${_arch}" CACHE STRING "" FORCE)
# compilers
set(CMAKE_C_COMPILER "${_target_dir}/bin/${_host}-clang" CACHE FILEPATH "" FORCE)
set(CMAKE_CXX_COMPILER "${_target_dir}/bin/${_host}-clang++" CACHE FILEPATH "" FORCE)
# tools
set(CMAKE_AR "${_target_dir}/bin/${_host}-ar" CACHE FILEPATH "" FORCE)
set(CMAKE_RANLIB "${_target_dir}/bin/${_host}-ranlib" CACHE FILEPATH "" FORCE)
set(CMAKE_STRIP "${_target_dir}/bin/${_host}-strip" CACHE FILEPATH "" FORCE)
set(CMAKE_INSTALL_NAME_TOOL "${_target_dir}/bin/${_host}-install_name_tool" CACHE FILEPATH "" FORCE)
# search paths
set(CMAKE_FIND_ROOT_PATH "${_sdk_dir}" "${_target_dir}")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

View file

@ -0,0 +1,366 @@
#!/usr/bin/env python3
"""Download and extract macOS SDK from Apple's Command Line Tools package.
Apple publishes Command Line Tools (CLT) packages via their publicly
accessible software update catalog. This script downloads the latest CLT,
extracts just the macOS SDK, and packages it as a .tar.gz tarball suitable
for osxcross.
No Apple ID or paid developer account required.
Usage:
python3 sdk-fetcher.py [output_dir]
The script prints the absolute path of the resulting tarball to stdout.
All progress / status messages go to stderr.
If a cached SDK tarball already exists in output_dir, it is reused.
Dependencies: python3 (>= 3.6), cpio, tar, gzip
Optional: bsdtar (libarchive-tools) or xar -- faster XAR extraction.
Falls back to a pure-Python XAR parser when neither is available.
"""
import glob
import gzip
import lzma
import os
import plistlib
import re
import shutil
import struct
import subprocess
import sys
import tempfile
import urllib.request
import xml.etree.ElementTree as ET
import zlib
# -- Configuration -----------------------------------------------------------
CATALOG_URLS = [
# Try newest catalog first; first successful fetch wins.
"https://swscan.apple.com/content/catalogs/others/"
"index-16-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-"
"mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz",
"https://swscan.apple.com/content/catalogs/others/"
"index-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-"
"mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz",
"https://swscan.apple.com/content/catalogs/others/"
"index-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-"
"mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz",
]
USER_AGENT = "Software%20Update"
# -- Helpers -----------------------------------------------------------------
def log(msg):
print(msg, file=sys.stderr, flush=True)
# -- 1) Catalog & URL discovery ----------------------------------------------
def find_sdk_pkg_url():
"""Search Apple catalogs for the latest CLTools_macOSNMOS_SDK.pkg URL."""
for cat_url in CATALOG_URLS:
short = cat_url.split("/index-")[1][:25] + "..."
log(f" Trying catalog: {short}")
try:
req = urllib.request.Request(cat_url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=60) as resp:
raw = gzip.decompress(resp.read())
catalog = plistlib.loads(raw)
except Exception as exc:
log(f" -> fetch failed: {exc}")
continue
products = catalog.get("Products", {})
candidates = []
for pid, product in products.items():
post_date = str(product.get("PostDate", ""))
for pkg in product.get("Packages", []):
url = pkg.get("URL", "")
size = pkg.get("Size", 0)
if "CLTools_macOSNMOS_SDK" in url and url.endswith(".pkg"):
candidates.append((post_date, url, size, pid))
if not candidates:
log(f" -> no CLTools SDK packages in this catalog, trying next...")
continue
candidates.sort(reverse=True)
_date, url, size, pid = candidates[0]
log(f"==> Found: CLTools_macOSNMOS_SDK (product {pid}, {size // 1048576} MB)")
return url
log("ERROR: No CLTools SDK packages found in any Apple catalog.")
sys.exit(1)
# -- 2) Download -------------------------------------------------------------
def download(url, dest):
"""Download *url* to *dest* with a basic progress indicator."""
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=600) as resp:
total = int(resp.headers.get("Content-Length", 0))
done = 0
with open(dest, "wb") as f:
while True:
chunk = resp.read(1 << 20)
if not chunk:
break
f.write(chunk)
done += len(chunk)
if total:
pct = done * 100 // total
log(f"\r {done // 1048576} / {total // 1048576} MB ({pct}%)")
log("")
# -- 3) XAR extraction -------------------------------------------------------
def extract_xar(pkg_path, dest_dir):
"""Extract a XAR (.pkg) archive -- external tool or pure-Python fallback."""
for tool in ("bsdtar", "xar"):
if shutil.which(tool):
log(f"==> Extracting .pkg with {tool}...")
r = subprocess.run([tool, "-xf", pkg_path, "-C", dest_dir],
capture_output=True)
if r.returncode == 0:
return
log(f" {tool} exited {r.returncode}, trying next method...")
log("==> Extracting .pkg with built-in Python XAR parser...")
_extract_xar_python(pkg_path, dest_dir)
def _extract_xar_python(pkg_path, dest_dir):
"""Pure-Python XAR extractor (no external dependencies)."""
with open(pkg_path, "rb") as f:
raw = f.read(28)
if len(raw) < 28:
raise ValueError("File too small to be a valid XAR archive")
magic, hdr_size, _ver, toc_clen, _toc_ulen, _ck = struct.unpack(
">4sHHQQI", raw,
)
if magic != b"xar!":
raise ValueError(f"Not a XAR file (magic: {magic!r})")
f.seek(hdr_size)
toc_xml = zlib.decompress(f.read(toc_clen))
heap_off = hdr_size + toc_clen
root = ET.fromstring(toc_xml)
toc = root.find("toc")
if toc is None:
raise ValueError("Malformed XAR: no <toc> element")
def _walk(elem, base):
for fe in elem.findall("file"):
name = fe.findtext("name", "")
ftype = fe.findtext("type", "file")
path = os.path.join(base, name)
if ftype == "directory":
os.makedirs(path, exist_ok=True)
_walk(fe, path)
continue
de = fe.find("data")
if de is None:
continue
offset = int(de.findtext("offset", "0"))
size = int(de.findtext("size", "0"))
enc_el = de.find("encoding")
enc = enc_el.get("style", "") if enc_el is not None else ""
os.makedirs(os.path.dirname(path), exist_ok=True)
f.seek(heap_off + offset)
if "gzip" in enc:
with open(path, "wb") as out:
out.write(zlib.decompress(f.read(size), 15 + 32))
elif "bzip2" in enc:
import bz2
with open(path, "wb") as out:
out.write(bz2.decompress(f.read(size)))
else:
with open(path, "wb") as out:
rem = size
while rem > 0:
blk = f.read(min(rem, 1 << 20))
if not blk:
break
out.write(blk)
rem -= len(blk)
_walk(toc, dest_dir)
# -- 4) Payload extraction (pbzx / gzip cpio) --------------------------------
def _pbzx_stream(path):
"""Yield decompressed chunks from a pbzx-compressed file."""
with open(path, "rb") as f:
if f.read(4) != b"pbzx":
raise ValueError("Not a pbzx file")
f.read(8)
while True:
hdr = f.read(16)
if len(hdr) < 16:
break
_usize, csize = struct.unpack(">QQ", hdr)
data = f.read(csize)
if len(data) < csize:
break
if csize == _usize:
yield data
else:
yield lzma.decompress(data)
def _gzip_stream(path):
"""Yield decompressed chunks from a gzip file."""
with gzip.open(path, "rb") as f:
while True:
chunk = f.read(1 << 20)
if not chunk:
break
yield chunk
def _raw_stream(path):
"""Yield raw 1 MiB chunks (last resort)."""
with open(path, "rb") as f:
while True:
chunk = f.read(1 << 20)
if not chunk:
break
yield chunk
def extract_payload(payload_path, out_dir):
"""Decompress a CLT Payload (pbzx or gzip cpio) into *out_dir*."""
with open(payload_path, "rb") as pf:
magic = pf.read(4)
if magic == b"pbzx":
log(" Payload format: pbzx (LZMA chunks)")
stream = _pbzx_stream(payload_path)
elif magic[:2] == b"\x1f\x8b":
log(" Payload format: gzip")
stream = _gzip_stream(payload_path)
else:
log(f" Payload format: unknown (magic: {magic.hex()}), trying raw cpio...")
stream = _raw_stream(payload_path)
proc = subprocess.Popen(
["cpio", "-id", "--quiet"],
stdin=subprocess.PIPE,
cwd=out_dir,
stderr=subprocess.PIPE,
)
for chunk in stream:
try:
proc.stdin.write(chunk)
except BrokenPipeError:
break
proc.stdin.close()
proc.wait()
# -- Main --------------------------------------------------------------------
def main():
output_dir = os.path.abspath(sys.argv[1]) if len(sys.argv) > 1 else os.getcwd()
os.makedirs(output_dir, exist_ok=True)
# Re-use a previously fetched SDK if present.
cached = glob.glob(os.path.join(output_dir, "MacOSX*.sdk.tar.*"))
if cached:
cached.sort()
result = os.path.realpath(cached[-1])
log(f"==> Using cached SDK: {os.path.basename(result)}")
print(result)
return
work = tempfile.mkdtemp(prefix="fetch-macos-sdk-")
try:
# 1 -- Locate SDK package URL from Apple's catalog
log("==> Searching Apple software-update catalogs...")
sdk_url = find_sdk_pkg_url()
# 2 -- Download (just the SDK component, ~55 MB)
pkg = os.path.join(work, "sdk.pkg")
log("==> Downloading CLTools SDK package...")
download(sdk_url, pkg)
# 3 -- Extract the flat .pkg (XAR format) to get the Payload
pkg_dir = os.path.join(work, "pkg")
os.makedirs(pkg_dir)
extract_xar(pkg, pkg_dir)
os.unlink(pkg)
# 4 -- Locate the Payload file
log("==> Locating SDK payload...")
sdk_payload = None
for dirpath, _dirs, files in os.walk(pkg_dir):
if "Payload" in files:
sdk_payload = os.path.join(dirpath, "Payload")
log(f" Found: {os.path.relpath(sdk_payload, pkg_dir)}")
break
if sdk_payload is None:
log("ERROR: No Payload found in extracted package")
sys.exit(1)
# 5 -- Decompress Payload -> cpio -> filesystem
sdk_root = os.path.join(work, "sdk")
os.makedirs(sdk_root)
log("==> Extracting SDK from payload (this may take a minute)...")
extract_payload(sdk_payload, sdk_root)
shutil.rmtree(pkg_dir)
# 6 -- Find MacOSX*.sdk directory
sdk_found = None
for dirpath, dirs, _files in os.walk(sdk_root):
for d in dirs:
if re.match(r"MacOSX\d+(\.\d+)?\.sdk$", d):
sdk_found = os.path.join(dirpath, d)
break
if sdk_found:
break
if not sdk_found:
log("ERROR: MacOSX*.sdk directory not found. Extracted contents:")
for dp, ds, fs in os.walk(sdk_root):
depth = dp.replace(sdk_root, "").count(os.sep)
if depth < 4:
log(f" {' ' * depth}{os.path.basename(dp)}/")
sys.exit(1)
sdk_name = os.path.basename(sdk_found)
log(f"==> Found: {sdk_name}")
# 7 -- Package as .tar.gz
tarball = os.path.join(output_dir, f"{sdk_name}.tar.gz")
log(f"==> Packaging: {sdk_name}.tar.gz ...")
subprocess.run(
["tar", "-czf", tarball, "-C", os.path.dirname(sdk_found), sdk_name],
check=True,
)
log(f"==> macOS SDK ready: {tarball}")
print(tarball)
finally:
shutil.rmtree(work, ignore_errors=True)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,10 @@
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_OSX_ARCHITECTURES arm64)
set(VCPKG_OSX_DEPLOYMENT_TARGET 13.0)
# osxcross toolchain
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE /opt/osxcross-toolchain.cmake)

View file

@ -0,0 +1,10 @@
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_OSX_ARCHITECTURES x86_64)
set(VCPKG_OSX_DEPLOYMENT_TARGET 13.0)
# osxcross toolchain
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE /opt/osxcross-toolchain.cmake)

64
container/run-linux.ps1 Normal file
View file

@ -0,0 +1,64 @@
# run-linux.ps1 — Build WoWee for Linux (amd64) inside a Docker container.
#
# Usage (run from project root):
# .\container\run-linux.ps1 [-RebuildImage]
#
# Environment variables:
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
param(
[switch]$RebuildImage
)
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$ProjectRoot = (Resolve-Path "$ScriptDir\..").Path
$ImageName = "wowee-builder-linux"
$BuildOutput = "$ProjectRoot\build\linux"
# Verify Docker is available
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Error "docker is not installed or not in PATH."
exit 1
}
# Build the image (skip if already present and -RebuildImage not given)
$imageExists = docker image inspect $ImageName 2>$null
if ($RebuildImage -or -not $imageExists) {
Write-Host "==> Building Docker image: $ImageName"
docker build `
-f "$ScriptDir\builder-linux.Dockerfile" `
-t $ImageName `
"$ScriptDir"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
} else {
Write-Host "==> Using existing Docker image: $ImageName"
}
# Create output directory on the host
New-Item -ItemType Directory -Force -Path $BuildOutput | Out-Null
Write-Host "==> Starting Linux build (output: $BuildOutput)"
$dockerArgs = @(
"run", "--rm",
"--mount", "type=bind,src=$ProjectRoot,dst=/src,readonly",
"--mount", "type=bind,src=$BuildOutput,dst=/out"
)
if ($env:WOWEE_FFX_SDK_REPO) {
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REPO=$env:WOWEE_FFX_SDK_REPO")
}
if ($env:WOWEE_FFX_SDK_REF) {
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REF=$env:WOWEE_FFX_SDK_REF")
}
$dockerArgs += $ImageName
& docker @dockerArgs
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-Host "==> Linux build complete. Artifacts in: $BuildOutput"

58
container/run-linux.sh Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env bash
# run-linux.sh — Build WoWee for Linux (amd64) inside a Docker container.
#
# Usage (run from project root):
# ./container/run-linux.sh [--rebuild-image]
#
# Environment variables:
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
# REBUILD_IMAGE — Set to 1 to force a fresh docker build (same as --rebuild-image)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
IMAGE_NAME="wowee-builder-linux"
BUILD_OUTPUT="${PROJECT_ROOT}/build/linux"
# Parse arguments
REBUILD_IMAGE="${REBUILD_IMAGE:-0}"
for arg in "$@"; do
case "$arg" in
--rebuild-image) REBUILD_IMAGE=1 ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
# Verify Docker is available
if ! command -v docker &>/dev/null; then
echo "Error: docker is not installed or not in PATH." >&2
exit 1
fi
# Build the image (skip if already present and --rebuild-image not given)
if [[ "$REBUILD_IMAGE" == "1" ]] || ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
echo "==> Building Docker image: ${IMAGE_NAME}"
docker build \
-f "${SCRIPT_DIR}/builder-linux.Dockerfile" \
-t "$IMAGE_NAME" \
"${SCRIPT_DIR}"
else
echo "==> Using existing Docker image: ${IMAGE_NAME}"
fi
# Create output directory on the host
mkdir -p "$BUILD_OUTPUT"
echo "==> Starting Linux build (output: ${BUILD_OUTPUT})"
docker run --rm \
--mount "type=bind,src=${PROJECT_ROOT},dst=/src,readonly" \
--mount "type=bind,src=${BUILD_OUTPUT},dst=/out" \
${WOWEE_FFX_SDK_REPO:+--env "WOWEE_FFX_SDK_REPO=${WOWEE_FFX_SDK_REPO}"} \
${WOWEE_FFX_SDK_REF:+--env "WOWEE_FFX_SDK_REF=${WOWEE_FFX_SDK_REF}"} \
"$IMAGE_NAME"
echo "==> Linux build complete. Artifacts in: ${BUILD_OUTPUT}"

71
container/run-macos.ps1 Normal file
View file

@ -0,0 +1,71 @@
# run-macos.ps1 — Cross-compile WoWee for macOS (arm64 or x86_64) inside a Docker container.
#
# Usage (run from project root):
# .\container\run-macos.ps1 [-RebuildImage] [-Arch arm64|x86_64]
#
# The macOS SDK is fetched automatically inside the Docker build from Apple's
# public software update catalog. No manual SDK download required.
#
# Environment variables:
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
param(
[switch]$RebuildImage,
[ValidateSet("arm64", "x86_64")]
[string]$Arch = "arm64"
)
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$ProjectRoot = (Resolve-Path "$ScriptDir\..").Path
$ImageName = "wowee-builder-macos"
$BuildOutput = "$ProjectRoot\build\macos"
# Verify Docker is available
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Error "docker is not installed or not in PATH."
exit 1
}
# Build the image (skip if already present and -RebuildImage not given)
$imageExists = docker image inspect $ImageName 2>$null
if ($RebuildImage -or -not $imageExists) {
Write-Host "==> Building Docker image: $ImageName"
Write-Host " (SDK will be fetched automatically from Apple's catalog)"
docker build `
-f "$ScriptDir\builder-macos.Dockerfile" `
-t $ImageName `
"$ScriptDir"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
} else {
Write-Host "==> Using existing Docker image: $ImageName"
}
# Create output directory on the host
New-Item -ItemType Directory -Force -Path $BuildOutput | Out-Null
Write-Host "==> Starting macOS cross-compile build (arch=$Arch, output: $BuildOutput)"
$dockerArgs = @(
"run", "--rm",
"--mount", "type=bind,src=$ProjectRoot,dst=/src,readonly",
"--mount", "type=bind,src=$BuildOutput,dst=/out",
"--env", "MACOS_ARCH=$Arch"
)
if ($env:WOWEE_FFX_SDK_REPO) {
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REPO=$env:WOWEE_FFX_SDK_REPO")
}
if ($env:WOWEE_FFX_SDK_REF) {
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REF=$env:WOWEE_FFX_SDK_REF")
}
$dockerArgs += $ImageName
& docker @dockerArgs
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-Host "==> macOS cross-compile build complete. Artifacts in: $BuildOutput"

74
container/run-macos.sh Executable file
View file

@ -0,0 +1,74 @@
#!/usr/bin/env bash
# run-macos.sh — Cross-compile WoWee for macOS (arm64 or x86_64) inside a Docker container.
#
# Usage (run from project root):
# ./container/run-macos.sh [--rebuild-image]
#
# The macOS SDK is fetched automatically inside the Docker build from Apple's
# public software update catalog. No manual SDK download required.
#
# Environment variables:
# MACOS_ARCH — Target arch: arm64 (default) or x86_64
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
# REBUILD_IMAGE — Set to 1 to force a fresh docker build (same as --rebuild-image)
#
# Toolchain: osxcross (Clang + Apple ld)
# vcpkg triplets: arm64-osx-cross (arm64) / x64-osx-cross (x86_64)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
IMAGE_NAME="wowee-builder-macos"
MACOS_ARCH="${MACOS_ARCH:-arm64}"
BUILD_OUTPUT="${PROJECT_ROOT}/build/macos"
# Parse arguments
REBUILD_IMAGE="${REBUILD_IMAGE:-0}"
for arg in "$@"; do
case "$arg" in
--rebuild-image) REBUILD_IMAGE=1 ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
# Validate arch
if [[ "$MACOS_ARCH" != "arm64" && "$MACOS_ARCH" != "x86_64" ]]; then
echo "Error: MACOS_ARCH must be 'arm64' or 'x86_64' (got: ${MACOS_ARCH})" >&2
exit 1
fi
# Verify Docker is available
if ! command -v docker &>/dev/null; then
echo "Error: docker is not installed or not in PATH." >&2
exit 1
fi
# Build the image (skip if already present and --rebuild-image not given)
if [[ "$REBUILD_IMAGE" == "1" ]] || ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
echo "==> Building Docker image: ${IMAGE_NAME}"
echo " (SDK will be fetched automatically from Apple's catalog)"
docker build \
-f "${SCRIPT_DIR}/builder-macos.Dockerfile" \
-t "$IMAGE_NAME" \
"${SCRIPT_DIR}"
else
echo "==> Using existing Docker image: ${IMAGE_NAME}"
fi
# Create output directory on the host
mkdir -p "$BUILD_OUTPUT"
echo "==> Starting macOS cross-compile build (arch=${MACOS_ARCH}, output: ${BUILD_OUTPUT})"
docker run --rm \
--mount "type=bind,src=${PROJECT_ROOT},dst=/src,readonly" \
--mount "type=bind,src=${BUILD_OUTPUT},dst=/out" \
--env "MACOS_ARCH=${MACOS_ARCH}" \
${WOWEE_FFX_SDK_REPO:+--env "WOWEE_FFX_SDK_REPO=${WOWEE_FFX_SDK_REPO}"} \
${WOWEE_FFX_SDK_REF:+--env "WOWEE_FFX_SDK_REF=${WOWEE_FFX_SDK_REF}"} \
"$IMAGE_NAME"
echo "==> macOS cross-compile build complete. Artifacts in: ${BUILD_OUTPUT}"

64
container/run-windows.ps1 Normal file
View file

@ -0,0 +1,64 @@
# run-windows.ps1 — Cross-compile WoWee for Windows (x86_64) inside a Docker container.
#
# Usage (run from project root):
# .\container\run-windows.ps1 [-RebuildImage]
#
# Environment variables:
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
param(
[switch]$RebuildImage
)
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$ProjectRoot = (Resolve-Path "$ScriptDir\..").Path
$ImageName = "wowee-builder-windows"
$BuildOutput = "$ProjectRoot\build\windows"
# Verify Docker is available
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Error "docker is not installed or not in PATH."
exit 1
}
# Build the image (skip if already present and -RebuildImage not given)
$imageExists = docker image inspect $ImageName 2>$null
if ($RebuildImage -or -not $imageExists) {
Write-Host "==> Building Docker image: $ImageName"
docker build `
-f "$ScriptDir\builder-windows.Dockerfile" `
-t $ImageName `
"$ScriptDir"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
} else {
Write-Host "==> Using existing Docker image: $ImageName"
}
# Create output directory on the host
New-Item -ItemType Directory -Force -Path $BuildOutput | Out-Null
Write-Host "==> Starting Windows cross-compile build (output: $BuildOutput)"
$dockerArgs = @(
"run", "--rm",
"--mount", "type=bind,src=$ProjectRoot,dst=/src,readonly",
"--mount", "type=bind,src=$BuildOutput,dst=/out"
)
if ($env:WOWEE_FFX_SDK_REPO) {
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REPO=$env:WOWEE_FFX_SDK_REPO")
}
if ($env:WOWEE_FFX_SDK_REF) {
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REF=$env:WOWEE_FFX_SDK_REF")
}
$dockerArgs += $ImageName
& docker @dockerArgs
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-Host "==> Windows cross-compile build complete. Artifacts in: $BuildOutput"

61
container/run-windows.sh Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/env bash
# run-windows.sh — Cross-compile WoWee for Windows (x86_64) inside a Docker container.
#
# Usage (run from project root):
# ./container/run-windows.sh [--rebuild-image]
#
# Environment variables:
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
# REBUILD_IMAGE — Set to 1 to force a fresh docker build (same as --rebuild-image)
#
# Toolchain: LLVM-MinGW (Clang + LLD) targeting x86_64-w64-mingw32-ucrt
# vcpkg triplet: x64-mingw-static
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
IMAGE_NAME="wowee-builder-windows"
BUILD_OUTPUT="${PROJECT_ROOT}/build/windows"
# Parse arguments
REBUILD_IMAGE="${REBUILD_IMAGE:-0}"
for arg in "$@"; do
case "$arg" in
--rebuild-image) REBUILD_IMAGE=1 ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
# Verify Docker is available
if ! command -v docker &>/dev/null; then
echo "Error: docker is not installed or not in PATH." >&2
exit 1
fi
# Build the image (skip if already present and --rebuild-image not given)
if [[ "$REBUILD_IMAGE" == "1" ]] || ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
echo "==> Building Docker image: ${IMAGE_NAME}"
docker build \
-f "${SCRIPT_DIR}/builder-windows.Dockerfile" \
-t "$IMAGE_NAME" \
"${SCRIPT_DIR}"
else
echo "==> Using existing Docker image: ${IMAGE_NAME}"
fi
# Create output directory on the host
mkdir -p "$BUILD_OUTPUT"
echo "==> Starting Windows cross-compile build (output: ${BUILD_OUTPUT})"
docker run --rm \
--mount "type=bind,src=${PROJECT_ROOT},dst=/src,readonly" \
--mount "type=bind,src=${BUILD_OUTPUT},dst=/out" \
${WOWEE_FFX_SDK_REPO:+--env "WOWEE_FFX_SDK_REPO=${WOWEE_FFX_SDK_REPO}"} \
${WOWEE_FFX_SDK_REF:+--env "WOWEE_FFX_SDK_REF=${WOWEE_FFX_SDK_REF}"} \
"$IMAGE_NAME"
echo "==> Windows cross-compile build complete. Artifacts in: ${BUILD_OUTPUT}"

View file

@ -1 +1 @@
IDI_APP_ICON ICON "assets\\wowee.ico" IDI_APP_ICON ICON "assets/wowee.ico"

View file

@ -10,6 +10,7 @@
"glew", "glew",
"glm", "glm",
"zlib", "zlib",
"ffmpeg" "ffmpeg",
"vulkan-headers"
] ]
} }