diff --git a/.gitignore b/.gitignore index 3d44015..c6d90c5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,8 @@ /packaging/peerspeak/ /packaging/*.pkg.tar.* /packaging/*.log + +# Windows installer build artifacts (the staged exe + compiled setup.exe); +# the .iss script and .ico are the tracked sources. +/packaging/windows/peerspeak.exe +/packaging/windows/output/ diff --git a/packaging/windows/README.md b/packaging/windows/README.md new file mode 100644 index 0000000..0efa542 --- /dev/null +++ b/packaging/windows/README.md @@ -0,0 +1,72 @@ +# PeerSpeak — Windows installer + +This directory builds a Windows setup installer for PeerSpeak using +[Inno Setup](https://jrsoftware.org/isinfo.php). + +PeerSpeak ships as a **single self-contained `peerspeak.exe`** — the GUI icon, +notification chimes, and avatar presets are all embedded in the binary +(`include_bytes!`), and the executable is statically linked against the GNU +runtime, so there are no extra DLLs to bundle. The installer payload is just the +`.exe` plus an `.ico` for the Start-menu / desktop shortcuts. + +## Files + +| File | Tracked | Purpose | +|------|---------|---------| +| `peerspeak.iss` | yes | Inno Setup script | +| `peerspeak.ico` | yes | multi-resolution app icon (from `assets/icons/*.png`) | +| `README.md` | yes | this file | +| `peerspeak.exe` | no (gitignored) | staged build artifact, copied from `target/x86_64-pc-windows-gnu/release/` | +| `output/peerspeak--setup.exe` | no (gitignored) | the compiled installer | + +## Build steps + +1. **Cross-compile the Windows binary** (from the repo root, inside the + `peerspeak-win` archlinux distrobox): + + ```sh + RUSTC_BOOTSTRAP=1 ./win-cross-build.sh -Z build-std=std,panic_abort + ``` + + This needs the `rust-src` component and the `x86_64-pc-windows-gnu` target + installed in that toolchain. The result is a statically-linked, + GUI-subsystem `.exe` (no stray console window). + +2. **Stage the binary** next to the script: + + ```sh + cp target/x86_64-pc-windows-gnu/release/peerspeak.exe packaging/windows/ + ``` + +3. **Regenerate the icon** if the source PNGs changed: + + ```sh + magick assets/icons/peerspeak-16.png assets/icons/peerspeak-24.png \ + assets/icons/peerspeak-32.png assets/icons/peerspeak-48.png \ + assets/icons/peerspeak-64.png assets/icons/peerspeak-128.png \ + assets/icons/peerspeak-256.png packaging/windows/peerspeak.ico + ``` + +4. **Compile the installer** with Inno Setup. On Linux this runs under Wine: + + ```sh + cd packaging/windows + wine ~/.wine/drive_c/InnoSetup6/ISCC.exe peerspeak.iss + ``` + + The installer lands at `output/peerspeak--setup.exe`. + +## What the installer does + +- Installs `peerspeak.exe` to `Program Files\PeerSpeak` (requires admin / one + UAC prompt). +- Creates a Start-menu shortcut, with an optional desktop shortcut. +- Optionally adds a Windows Firewall allow-rule for PeerSpeak (recommended — + iroh uses UDP hole-punching, so this avoids a mid-call firewall prompt). The + rule is removed on uninstall. +- Provides a standard uninstaller. + +> **Note:** the installer and the binary are **not code-signed**, so Windows +> SmartScreen will show an "unknown publisher" warning on first run. The user +> clicks *More info → Run anyway*. Removing this warning requires a paid +> code-signing certificate. diff --git a/packaging/windows/peerspeak.ico b/packaging/windows/peerspeak.ico new file mode 100644 index 0000000..880f70d Binary files /dev/null and b/packaging/windows/peerspeak.ico differ diff --git a/packaging/windows/peerspeak.iss b/packaging/windows/peerspeak.iss new file mode 100644 index 0000000..6fab24c --- /dev/null +++ b/packaging/windows/peerspeak.iss @@ -0,0 +1,63 @@ +; Inno Setup script for PeerSpeak (Windows installer). +; +; PeerSpeak is a single self-contained binary: the GUI icon, notification +; chimes, and avatar presets are all embedded in the .exe (include_bytes!), +; so the only payload here is peerspeak.exe plus an .ico for the shortcuts. +; +; Build (under Wine on Linux, or native Windows): +; wine "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" peerspeak.iss +; Output lands in .\output\peerspeak--setup.exe +; +; The peerspeak.exe is cross-compiled with win-cross-build.sh +; (x86_64-pc-windows-gnu, statically linked -- no extra DLLs needed). + +#define MyAppName "PeerSpeak" +#define MyAppVersion "0.2.0" +#define MyAppPublisher "mollusk" +#define MyAppExeName "peerspeak.exe" + +[Setup] +; A stable AppId keeps upgrades/uninstall tracking consistent across versions. +AppId={{2754D6C1-C8A4-4B13-9824-2D303439739D} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +DefaultDirName={autopf}\{#MyAppName} +DefaultGroupName={#MyAppName} +DisableProgramGroupPage=yes +UninstallDisplayIcon={app}\{#MyAppExeName} +SetupIconFile=peerspeak.ico +Compression=lzma2/max +SolidCompression=yes +WizardStyle=modern +OutputDir=output +OutputBaseFilename=peerspeak-{#MyAppVersion}-setup +; Program Files install + firewall rule both need elevation. +PrivilegesRequired=admin +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked +Name: "firewall"; Description: "Allow PeerSpeak through Windows Firewall (recommended for voice calls)"; GroupDescription: "Network:" + +[Files] +Source: "peerspeak.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: "peerspeak.ico"; DestDir: "{app}"; Flags: ignoreversion + +[Icons] +Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\peerspeak.ico" +Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" +Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\peerspeak.ico"; Tasks: desktopicon + +[Run] +; iroh uses UDP hole-punching; pre-authorizing avoids a mid-call firewall prompt. +Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall add rule name=""PeerSpeak"" dir=in action=allow program=""{app}\{#MyAppExeName}"" enable=yes profile=any"; Flags: runhidden; Tasks: firewall +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#MyAppName}}"; Flags: nowait postinstall skipifsilent + +[UninstallRun] +Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall delete rule name=""PeerSpeak"""; Flags: runhidden; RunOnceId: "DelPeerSpeakFirewall" diff --git a/src/main.rs b/src/main.rs index af0e8fb..9a42670 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,7 @@ +// On Windows, suppress the extra console window for release GUI builds while +// keeping it in debug builds so stderr/panics stay visible during development. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + fn main() { if let Err(e) = peerspeak::app::run_gui() { eprintln!("Error running GUI: {:?}", e); diff --git a/win-cross-build.sh b/win-cross-build.sh new file mode 100755 index 0000000..e063aa5 --- /dev/null +++ b/win-cross-build.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Cross-compile peerspeak to x86_64-pc-windows-gnu (run inside the peerspeak-win distrobox). +# Produces a statically-linked, self-contained .exe (no extra DLLs) for the +# Windows installer in packaging/windows/. Requires the rust-src component and +# the x86_64-pc-windows-gnu target; invoke with build-std for the static link: +# RUSTC_BOOTSTRAP=1 ./win-cross-build.sh -Z build-std=std,panic_abort +set -euo pipefail +cd "$(dirname "$0")" + +export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc +export CC_x86_64_pc_windows_gnu=x86_64-w64-mingw32-gcc +export CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++ +export AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar +# Bundled libopus declares cmake_minimum_required < 3.5; cmake 4.x refuses it. +export CMAKE_POLICY_VERSION_MINIMUM=3.5 + +cargo build --release --target x86_64-pc-windows-gnu --bin peerspeak "$@"