diff --git a/scripts/build-windows.sh b/scripts/build-windows.sh new file mode 100755 index 0000000..785d323 --- /dev/null +++ b/scripts/build-windows.sh @@ -0,0 +1,156 @@ +#!/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 "========================================================"