#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── # build-windows.sh # # Cross-compiles photo-converter for Windows (x86_64) using MinGW-w64. # Produces a self-contained directory with photo-converter.exe and all # required DLLs ready for distribution. # # Prerequisites (Ubuntu/Debian): # sudo apt install mingw-w64 cmake ninja-build # # Install MinGW-w64 cross builds of OpenCV, LibRaw, Qt6: # # Option A – compile from source with the MinGW toolchain. # # Option B – use an MXE (M cross environment) prefix. # # https://mxe.cc/ # # Option C – use pre-built MinGW packages from a provider such as # # https://github.com/brechtsanders/winlibs_mingw # # Usage: # bash scripts/build-windows.sh [BUILD_TYPE] [DEPS_PREFIX] # # BUILD_TYPE Release (default) | Debug | RelWithDebInfo # DEPS_PREFIX Path to cross-compiled dependencies # (default: /usr/x86_64-w64-mingw32) # # Example with a custom MXE prefix: # bash scripts/build-windows.sh Release /opt/mxe/usr/x86_64-w64-mingw32.static # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail # ── Configuration ───────────────────────────────────────────────────────────── BUILD_TYPE="${1:-Release}" DEPS_PREFIX="${2:-/usr/x86_64-w64-mingw32}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" BUILD_DIR="${PROJECT_ROOT}/build-windows" DIST_DIR="${PROJECT_ROOT}/dist-windows" TOOLCHAIN="${PROJECT_ROOT}/cmake/toolchain-mingw64.cmake" echo "========================================================" echo " photo-converter Windows Cross-Compilation" echo "========================================================" echo " Project root : ${PROJECT_ROOT}" echo " Build dir : ${BUILD_DIR}" echo " Build type : ${BUILD_TYPE}" echo " Deps prefix : ${DEPS_PREFIX}" echo "========================================================" # ── Verify prerequisites ────────────────────────────────────────────────────── for tool in x86_64-w64-mingw32-g++ cmake ninja; do if ! command -v "${tool}" &>/dev/null; then echo "ERROR: '${tool}' not found in PATH." >&2 echo "Install with: sudo apt install mingw-w64 cmake ninja-build" >&2 exit 1 fi done # ── CMake configure ────────────────────────────────────────────────────────── cmake \ -B "${BUILD_DIR}" \ -G Ninja \ -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN}" \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ -DCMAKE_INSTALL_PREFIX="${DIST_DIR}" \ -DCMAKE_PREFIX_PATH="${DEPS_PREFIX}" \ -DBUILD_TESTS=OFF \ -DBUILD_GUI=ON \ "${PROJECT_ROOT}" # ── Build ───────────────────────────────────────────────────────────────────── cmake --build "${BUILD_DIR}" -- -j"$(nproc)" # ── Install to dist dir ─────────────────────────────────────────────────────── cmake --install "${BUILD_DIR}" # ── Collect runtime DLLs ───────────────────────────────────────────────────── # MinGW runtime and dependency DLLs must ship alongside the .exe. BIN_DIR="${DIST_DIR}/bin" mkdir -p "${BIN_DIR}" echo "" echo "Collecting runtime DLLs..." # Helper: copy a DLL if it exists in the sysroot. copy_dll() { local dll_name="$1" local search_paths=( "${DEPS_PREFIX}/bin" "${DEPS_PREFIX}/lib" "/usr/lib/gcc/x86_64-w64-mingw32" "/usr/x86_64-w64-mingw32/lib" ) for dir in "${search_paths[@]}"; do if [[ -f "${dir}/${dll_name}" ]]; then cp -v "${dir}/${dll_name}" "${BIN_DIR}/" return 0 fi done echo " WARNING: ${dll_name} not found (may be statically linked)" >&2 return 0 } # MinGW runtime DLLs (may be statically linked via -static-libgcc/-static-libstdc++) copy_dll "libgcc_s_seh-1.dll" || true copy_dll "libstdc++-6.dll" || true copy_dll "libwinpthread-1.dll" || true # OpenCV DLLs (names include version number – use glob) for dll in "${DEPS_PREFIX}/bin"/libopencv_*.dll; do [[ -f "${dll}" ]] && cp -v "${dll}" "${BIN_DIR}/" done # LibRaw copy_dll "libraw.dll" || true copy_dll "libraw-23.dll" || true # Qt6 DLLs for qt_dll in Qt6Core Qt6Gui Qt6Widgets; do copy_dll "${qt_dll}.dll" || true done # Qt platform plugin QT_PLATFORMS_SRC="${DEPS_PREFIX}/share/qt6/plugins/platforms" if [[ -d "${QT_PLATFORMS_SRC}" ]]; then mkdir -p "${BIN_DIR}/platforms" cp -v "${QT_PLATFORMS_SRC}/qwindows.dll" "${BIN_DIR}/platforms/" 2>/dev/null || true fi # ── Strip release binaries ──────────────────────────────────────────────────── if [[ "${BUILD_TYPE}" == "Release" ]]; then echo "" echo "Stripping debug symbols from release binaries..." x86_64-w64-mingw32-strip --strip-all "${BIN_DIR}/photo-converter.exe" 2>/dev/null || true find "${BIN_DIR}" -name "*.dll" -exec x86_64-w64-mingw32-strip --strip-unneeded {} \; 2>/dev/null || true fi # ── Copy example config ─────────────────────────────────────────────────────── cp -v "${PROJECT_ROOT}/config.ini" "${DIST_DIR}/config.ini.example" # ── Create ZIP archive ──────────────────────────────────────────────────────── ARCHIVE="${PROJECT_ROOT}/photo-converter-windows-x64-${BUILD_TYPE}.zip" if command -v zip &>/dev/null; then (cd "${PROJECT_ROOT}" && zip -r "${ARCHIVE}" "dist-windows/") echo "" echo "Archive: ${ARCHIVE}" else echo "(zip not available; skipping archive creation)" fi echo "" echo "========================================================" echo " Build complete!" echo " Output: ${DIST_DIR}" echo " Run: wine ${BIN_DIR}/photo-converter.exe --help" echo "========================================================"