Files
proton-stream/packaging/README.md
T
2026-08-03 09:38:01 +02:00

7.4 KiB

Packaging

Everything here is driven by two scripts. scripts/build.sh covers Linux and macOS; scripts/build.ps1 covers Windows, because the .exe and the .msi need MSVC and WiX and neither is reachable from a Unix host.

Artifact Command Host it must run on
dist/proton-stream-<ver>-<triple>.tar.gz scripts/build.sh tarball Linux or macOS
dist/proton-stream_<ver>-1_amd64.deb scripts/build.sh deb any Linux
dist/proton-stream-<ver>-1.x86_64.rpm scripts/build.sh rpm any Linux with rpmbuild
packaging/*.pkg.tar.zst cd packaging && makepkg -fi Arch
dist/Proton Stream.app + .dmg scripts/build.sh dmg macOS
dist/windows/proton-stream.exe + .zip scripts\build.ps1 -Artifacts zip Windows
dist/proton-stream-<ver>-x64.msi scripts\build.ps1 -Artifacts msi Windows

scripts/build.sh with no arguments produces everything the host can.

How it fits together

One cargo build feeds every Linux and macOS artifact. The binaries are compiled once, staged into a filesystem image at dist/stage — rooted so that dist/stage/usr/bin/pstr is exactly where the file lands on the target system — and each packager copies out of that image. A .deb and an .rpm from the same run are therefore the same bytes, and neither dpkg-deb nor rpmbuild needs a Rust toolchain or libmpv headers.

--skip-build reuses dist/stage, which is what you want when iterating on a control file or a spec.

The Arch package is the exception: makepkg compiles in place from the checkout, by Arch convention and to match ../proton-drive-linux. It ignores dist/stage entirely. scripts/build.sh arch runs it for you and copies the result into dist/, but it is not part of all — it recompiles, and most hosts are not Arch.

Runtime dependencies, and why they are what they are

Why
libmpv ≥ 0.34 The player. pstr-player links it directly and drives it through stream_cb; nothing here shells out to mpv(1), so packages depend on the library, not the player binary.
libsecret / Secret Service The share URL fragment and the custom password are secrets and live in the OS credential store, never in shares.json.
libGL, libxkbcommon, libX11, libwayland-client eframe on glow, with both display backends compiled in.

Not dependencies, deliberately: SQLite (rusqlite is bundled) and OpenSSL (TLS is rustls with webpki-roots). Both are inside the binary.

Per packager that is:

  • Debian — libmpv2 | libmpv1 (trixie | bookworm), libsecret-1-0, libgl1, libxkbcommon0, libx11-6, libwayland-client0.
  • RPM — mpv-libs, libsecret, mesa-libGL, libxkbcommon, libX11, libwayland-client.
  • Arch — mpv (which carries both libmpv.so and its headers), libsecret.

The .deb and .rpm are built from binaries compiled on the packaging host, so they inherit its glibc. A package built on Arch installs on Fedora rawhide but not on an older distribution — build on the oldest one you intend to support, or in that distribution's container.

AutoReqProv: no in the spec is for the same reason: the ELF scan would generate soname requirements against whatever the packaging host happens to have, so the explicit Requires list stands instead.

Windows

libmpv is the whole difficulty. libmpv2-sys emits cargo:rustc-link-lib=mpv, so the linker wants an import library named mpv.lib and the process wants libmpv-2.dll at run time. Neither ships with mpv's Windows player build — both come from the separate mpv-dev archive:

https://sourceforge.net/projects/mpv-player-windows/files/libmpv/

Unpack it and point the script at it:

scripts\build.ps1 -MpvDev C:\mpv-dev

If that directory has mpv.def but no mpv.lib, the script generates the import library with lib.exe (/name:libmpv-2.dll, so it references the DLL that actually ships). It finds lib.exe on PATH or imports the MSVC environment via vswhere, so a plain PowerShell session works.

There is no static libmpv worth shipping, so "standalone exe" means the exe plus libmpv-2.dll beside it — that pair, plus pstr.exe and the README, is what the portable .zip contains and what the .msi installs.

The MSI needs the WiX toolset; the script installs it (dotnet tool install --global wix) and adds the UI extension if they are missing. It installs to Program Files\Proton Stream, puts a Start Menu shortcut on the GUI, and adds the install directory to the system PATH so pstr works from a shell — that PATH entry is tied to pstr.exe's component, so uninstalling removes it.

UpgradeCode in proton-stream.wxs is fixed forever: it is what makes 0.2.0 replace 0.1.0 rather than install beside it. Never regenerate it.

Signing is opt-in: -SignCertThumbprint <thumbprint> signs both executables and the MSI.

macOS

scripts/build.sh dmg produces Proton Stream.app inside a drag-to-Applications disk image.

By default it copies libmpv and its non-system dependency closure into Contents/Frameworks and rewrites every install name, because a .dmg that requires the recipient to have run brew install mpv is not a .dmg anyone can use. The bundle is then ad-hoc signed — every install_name_tool edit invalidates the signature, and an unsigned-but-modified binary is killed outright on arm64. Pass --no-bundle-libs to link against the host's libmpv instead.

The .icns is rasterized from io.narl.proton-stream.svg when rsvg-convert or magick is available; without either, the bundle ships without an icon.

Files

PKGBUILD Arch, in-tree makepkg build
proton-stream.spec RPM, installs from dist/stage
io.narl.proton-stream.desktop / .svg Freedesktop entry and icon
macos/Info.plist.in .app bundle plist; @VERSION@/@BIN@/@APP_ID@ substituted at build time
windows/proton-stream.wxs WiX v4+ installer definition
windows/proton-stream.ico MSI and shortcut icon, rasterized from the SVG
windows/license.rtf Fallback for the installer's license dialog; the repo's own LICENSE is preferred when it exists

The Debian control file has no template — scripts/build.sh writes it, because almost every field in it is computed.

Releasing

A release is a v<version> tag on main; .github/workflows/release.yml does the rest. It runs the CI gate and scripts/build.sh tarball deb rpm on Linux, scripts\build.ps1 -Artifacts zip,msi on Windows, and publishes both sets plus a SHA256SUMS to a GitHub release. So the steps are:

# 1. bump Cargo.toml's workspace version *and* packaging/PKGBUILD's pkgver
# 2. commit on main, then
git tag -a v0.1.0 -m 'proton-stream 0.1.0'
git push origin main --follow-tags

The workflow refuses to publish a tag whose commit is not an ancestor of main, or whose version disagrees with Cargo.toml or the PKGBUILD — the artifacts are named from the manifest, so a drifted tag would ship a file called something other than the tag. It also refuses a Cargo.toml carrying [patch.crates-io]: a release builds against the published SDK crates, never a sibling checkout.

workflow_dispatch runs everything except the publish step, which is how to rehearse without burning a tag.

Not covered by the workflow: the Arch package (makepkg compiles in place, so it wants an Arch host) and the macOS .app/.dmg (no runner for it here yet). Both are still scripts/build.sh arch and scripts/build.sh dmg by hand.