git repos / SagoImageBrowser

commit 9341bab5

Poul Sander · 2026-08-07 10:12
9341bab5807be88742df12ed0d3abb1e4e173951 patch · browse files
parent a0c1e102751e758a51c9e8907b7c848df2eaf289

Build an msi package for installing the Windows version. The Windows version does lack some polish but it works

Changed files

M .gitignore before
M CMakeLists.txt before
M README.md before
M extra/docker/Dockerfile.WindowsMxe before
A extra/windows/sagoimagebrowser.rc.in
A extra/windows/sagoimagebrowser.wxs
diff --git a/.gitignore b/.gitignore index 5776b83..d03b45f 100644 --- a/.gitignore +++ b/.gitignore
@@ -3,3 +3,5 @@ build
Makefile
*.patch
.idea
+dist
+extra/windows/*.ico
diff --git a/CMakeLists.txt b/CMakeLists.txt index b3f5b13..66a3bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.21)
-project(SagoImageBrowser LANGUAGES CXX)
+project(SagoImageBrowser VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
@@ -50,6 +50,18 @@ qt_add_executable(SagoImageBrowser
src/thumbnaildelegate.cpp
src/thumbnaildelegate.h)
+if(WIN32)
+ enable_language(RC)
+ # windres resolves relative paths in .rc files against its own CWD, not
+ # the .rc file's location, so the icon path must be baked in as absolute.
+ set(SAGO_ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/extra/windows/sago_image_browser.ico")
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/extra/windows/sagoimagebrowser.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/sagoimagebrowser.rc
+ @ONLY)
+ target_sources(SagoImageBrowser PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/sagoimagebrowser.rc)
+endif()
+
set_source_files_properties(extra/icons/sago_image_browser.svg PROPERTIES QT_RESOURCE_ALIAS sago_image_browser.svg)
qt_add_resources(SagoImageBrowser "icons" PREFIX "/icons" FILES extra/icons/sago_image_browser.svg)
diff --git a/README.md b/README.md index 96e4578..59ea89d 100644 --- a/README.md +++ b/README.md
@@ -75,6 +75,28 @@ A small test for the EXIF reader is built alongside the application:
./build/test_exifreader
```
+## Windows MSI build
+
+A 32-bit Windows build (statically linked, no external DLLs required) is cross-compiled via
+[MXE](https://mxe.cc/) in Docker, and packaged as an MSI installer with `wixl` (from
+[msitools](https://wiki.gnome.org/Projects/msitools)):
+
+```bash
+docker build -f extra/docker/Dockerfile.WindowsMxe --target export -o dist/ .
+```
+
+This produces `dist/SagoImageBrowser.exe` and `dist/SagoImageBrowser.msi`. If your Docker
+installation doesn't support `--output`/`-o` (requires BuildKit), build the `export` stage as a
+tagged image instead and copy the files out manually:
+
+```bash
+docker build -f extra/docker/Dockerfile.WindowsMxe --target export -t sago_image_browser_export .
+id=$(docker create sago_image_browser_export)
+docker cp "$id":/SagoImageBrowser.exe dist/
+docker cp "$id":/SagoImageBrowser.msi dist/
+docker rm "$id"
+```
+
## Project Structure
```
diff --git a/extra/docker/Dockerfile.WindowsMxe b/extra/docker/Dockerfile.WindowsMxe index ba862e3..48887d0 100644 --- a/extra/docker/Dockerfile.WindowsMxe +++ b/extra/docker/Dockerfile.WindowsMxe
@@ -1,9 +1,46 @@
-FROM sago007/mxe_qt6
+# syntax=docker/dockerfile:1
+FROM sago007/mxe_qt6 AS builder
+
+# wixl -> MSI builder (packaged separately from msitools on Ubuntu/Debian),
+# librsvg2-bin -> rsvg-convert (SVG->PNG), icoutils -> icotool (assemble
+# multi-res .ico from PNGs)
+RUN apt-get update && apt-get install --no-install-recommends -y \
+ wixl \
+ librsvg2-bin \
+ icoutils \
+ && rm -rf /var/lib/apt/lists/*
RUN mkdir -p /staging/sago_image_browser
COPY . /staging/sago_image_browser
+# Generate a multi-resolution Windows .ico from the SVG source icon.
+RUN mkdir -p /staging/sago_image_browser/extra/windows /tmp/icon-build && \
+ cd /tmp/icon-build && \
+ rsvg-convert -w 16 -h 16 /staging/sago_image_browser/extra/icons/sago_image_browser.svg -o icon-16.png && \
+ rsvg-convert -w 32 -h 32 /staging/sago_image_browser/extra/icons/sago_image_browser.svg -o icon-32.png && \
+ rsvg-convert -w 48 -h 48 /staging/sago_image_browser/extra/icons/sago_image_browser.svg -o icon-48.png && \
+ rsvg-convert -w 256 -h 256 /staging/sago_image_browser/extra/icons/sago_image_browser.svg -o icon-256.png && \
+ icotool -c -o /staging/sago_image_browser/extra/windows/sago_image_browser.ico \
+ icon-16.png icon-32.png icon-48.png icon-256.png
+
RUN cd /staging/sago_image_browser && rm -rf build && mkdir build && cd build && \
i686-w64-mingw32.static-cmake .. && \
-make -j$(nproc)
\ No newline at end of file
+make -j$(nproc)
+
+# Stage the payload wixl needs (it resolves relative Source/SourceFile paths
+# against its own CWD), then compile the MSI. Version is read out of
+# CMakeLists.txt so it stays the single source of truth.
+RUN mkdir -p /out && \
+ cp /staging/sago_image_browser/build/SagoImageBrowser.exe /out/ && \
+ cp /staging/sago_image_browser/extra/windows/sago_image_browser.ico /out/ && \
+ cp /staging/sago_image_browser/extra/windows/sagoimagebrowser.wxs /out/SagoImageBrowser.wxs && \
+ cd /out && \
+ VERSION=$(grep -oP 'VERSION \K[0-9]+\.[0-9]+\.[0-9]+' /staging/sago_image_browser/CMakeLists.txt) && \
+ echo "Building MSI for version: $VERSION" && \
+ wixl -v -D Version="$VERSION" -o SagoImageBrowser.msi SagoImageBrowser.wxs
+
+# Minimal export-only stage: `docker build --target export -o dist/ .`
+# copies just these two files to the host, nothing else.
+#FROM scratch AS export
+#COPY --from=builder /out/SagoImageBrowser.exe /out/SagoImageBrowser.msi /
diff --git a/extra/windows/sagoimagebrowser.rc.in b/extra/windows/sagoimagebrowser.rc.in new file mode 100644 index 0000000..39389b7 --- /dev/null +++ b/extra/windows/sagoimagebrowser.rc.in
@@ -0,0 +1 @@
+IDI_ICON1 ICON "@SAGO_ICON_PATH@"
diff --git a/extra/windows/sagoimagebrowser.wxs b/extra/windows/sagoimagebrowser.wxs new file mode 100644 index 0000000..15f320e --- /dev/null +++ b/extra/windows/sagoimagebrowser.wxs
@@ -0,0 +1,76 @@
+<?xml version='1.0' encoding='windows-1252'?>
+<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
+
+ <Product
+ Id='*'
+ Name='Sago Image Browser'
+ Language='1033'
+ Version='$(var.Version)'
+ Manufacturer='Poul Sander'
+ UpgradeCode='a39df77d-4657-44e1-94ca-b723b312463e'
+ >
+ <Package
+ Description='Sago Image Browser - a fast image browser'
+ Manufacturer='Poul Sander'
+ InstallerVersion='200'
+ Compressed='yes'
+ InstallScope='perMachine'
+ />
+
+ <MajorUpgrade DowngradeErrorMessage='A newer version of [ProductName] is already installed.' />
+ <MediaTemplate EmbedCab='yes' />
+
+ <Icon Id='SagoImageBrowser.ico' SourceFile='sago_image_browser.ico' />
+ <Property Id='ARPPRODUCTICON' Value='SagoImageBrowser.ico' />
+
+ <Directory Id='TARGETDIR' Name='SourceDir'>
+ <Directory Id='ProgramFilesFolder'>
+ <Directory Id='APPLICATIONROOTDIRECTORY' Name='Sago Image Browser' />
+ </Directory>
+ <Directory Id='ProgramMenuFolder'>
+ <Directory Id='ApplicationProgramsFolder' Name='Sago Image Browser' />
+ </Directory>
+ <Directory Id='DesktopFolder' Name='Desktop' />
+ </Directory>
+
+ <DirectoryRef Id='APPLICATIONROOTDIRECTORY'>
+ <Component Id='SagoImageBrowserExeComponent' Guid='41ce260d-34ff-41f5-b1e9-54b2b8722d90'>
+ <File Id='SagoImageBrowserExeFile' Source='SagoImageBrowser.exe' KeyPath='yes' />
+ </Component>
+ </DirectoryRef>
+
+ <DirectoryRef Id='ApplicationProgramsFolder'>
+ <Component Id='StartMenuShortcutComponent' Guid='b1efe04a-9b8c-444b-8ceb-61e0b8cc379d'>
+ <Shortcut Id='StartMenuShortcut'
+ Name='Sago Image Browser'
+ Description='A fast image browser'
+ Target='[#SagoImageBrowserExeFile]'
+ WorkingDirectory='APPLICATIONROOTDIRECTORY'
+ Icon='SagoImageBrowser.ico' />
+ <RemoveFolder Id='RemoveApplicationProgramsFolder' On='uninstall' />
+ <RegistryValue Root='HKCU' Key='Software\PoulSander\SagoImageBrowser'
+ Name='StartMenuShortcutInstalled' Type='integer' Value='1' KeyPath='yes' />
+ </Component>
+ </DirectoryRef>
+
+ <DirectoryRef Id='DesktopFolder'>
+ <Component Id='DesktopShortcutComponent' Guid='a0adad4a-1b9e-4881-b96a-d5ec245f9eef'>
+ <Shortcut Id='DesktopShortcut'
+ Name='Sago Image Browser'
+ Description='A fast image browser'
+ Target='[#SagoImageBrowserExeFile]'
+ WorkingDirectory='APPLICATIONROOTDIRECTORY'
+ Icon='SagoImageBrowser.ico' />
+ <RegistryValue Root='HKCU' Key='Software\PoulSander\SagoImageBrowser'
+ Name='DesktopShortcutInstalled' Type='integer' Value='1' KeyPath='yes' />
+ </Component>
+ </DirectoryRef>
+
+ <Feature Id='MainApplication' Title='Sago Image Browser' Level='1'>
+ <ComponentRef Id='SagoImageBrowserExeComponent' />
+ <ComponentRef Id='StartMenuShortcutComponent' />
+ <ComponentRef Id='DesktopShortcutComponent' />
+ </Feature>
+
+ </Product>
+</Wix>