feat(windows-client): cross-compile + ship ARM64 (aarch64) off the x64 runner
apple / swift (push) Successful in 55s
ci / rust (push) Successful in 1m20s
ci / web (push) Successful in 29s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m7s
ci / docs-site (push) Successful in 30s
android / android (push) Successful in 3m20s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m52s
deb / build-publish (push) Successful in 3m40s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 3m16s
decky / build-publish (push) Successful in 12s
ci / bench (push) Successful in 4m58s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 3m21s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m34s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m38s
docker / deploy-docs (push) Successful in 18s
apple / swift (push) Successful in 55s
ci / rust (push) Successful in 1m20s
ci / web (push) Successful in 29s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m7s
ci / docs-site (push) Successful in 30s
android / android (push) Successful in 3m20s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m52s
deb / build-publish (push) Successful in 3m40s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 3m16s
decky / build-publish (push) Successful in 12s
ci / bench (push) Successful in 4m58s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 3m21s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m34s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m38s
docker / deploy-docs (push) Successful in 18s
windows.yml + windows-msix.yml gain an x86_64/aarch64 target matrix. ARM64 is cross-compiled on the one x64 Windows runner — the x64 MSVC toolset ships the ARM64 cross compiler, aarch64-pc-windows-msvc is tier-2 with host tools, and SDL3/libopus (build-from-source) cross-compile cleanly. The only arch-specific external dep is FFmpeg's import libs: the matrix points FFMPEG_DIR at a per-arch tree (x64 C:\Users\Public\ffmpeg, arm64 C:\Users\Public\ffmpeg-arm64, both FFmpeg 7.x / avcodec-61). Per-arch short CARGO_TARGET_DIR avoids a shared target dir; fmt + test run only for x64 (aarch64 can't execute on the x64 host). pack-msix.ps1 gains -Arch x64|arm64 (stamps the manifest ProcessorArchitecture, arch-suffixes the .msix/.cer); windows-msix.yml matrixes both arches and publishes ..._x64.msix / ..._arm64.msix. setup-windows-runner.ps1 provisions the rustup target + the ARM64 FFmpeg tree (idempotent). Verified live on the runner (home-windows-1): debug+release cross-build green, clippy -D warnings green, and MSIX pack produces a valid arm64 package (manifest arch=arm64; bundled exe/SDL3/avcodec/reactor-bootstrap all PE machine 0xAA64). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,22 @@
|
|||||||
# Build the punktfunk Windows client as a signed MSIX and publish it to Gitea's generic package
|
# Build the punktfunk Windows client as signed MSIX packages (x64 + ARM64) and publish them to
|
||||||
# registry, so Windows boxes can download + install a real package (Start tile, clean
|
# Gitea's generic package registry, so Windows boxes can download + install a real package (Start
|
||||||
# install/uninstall) instead of a loose exe. Runs on the self-hosted Windows runner (host mode;
|
# tile, clean install/uninstall) instead of a loose exe. Runs on the self-hosted Windows runner
|
||||||
# scripts/ci/setup-windows-runner.ps1) — the MSVC/WinUI/FFmpeg toolchain + the Windows SDK's
|
# (host mode; scripts/ci/setup-windows-runner.ps1) — the MSVC/WinUI/FFmpeg toolchain + the Windows
|
||||||
# makeappx/signtool are baked into the runner's daemon env, same as windows.yml.
|
# SDK's makeappx/signtool are baked into the runner's daemon env, same as windows.yml.
|
||||||
|
#
|
||||||
|
# Both arches come off the ONE x64 runner: x86_64 natively, aarch64 cross-compiled (the x64 MSVC
|
||||||
|
# toolset has the ARM64 cross compiler; the matrix points FFMPEG_DIR at the ARM64 FFmpeg tree). See
|
||||||
|
# windows.yml for the cross-build rationale + the BOM/MAX_PATH runner gotchas.
|
||||||
#
|
#
|
||||||
# Registry (public, unom org): https://git.unom.io/unom/-/packages (generic group)
|
# Registry (public, unom org): https://git.unom.io/unom/-/packages (generic group)
|
||||||
# Packaging internals: clients/windows/packaging/README.md. BOM/MAX_PATH runner
|
# Packaging internals: clients/windows/packaging/README.md.
|
||||||
# gotchas baked into the daemon env + windows.yml: see that workflow.
|
|
||||||
#
|
#
|
||||||
# Versioning — MSIX requires a strictly 4-part numeric version (no ~/- suffixes), so:
|
# Versioning — MSIX requires a strictly 4-part numeric version (no ~/- suffixes), so:
|
||||||
# win-vX.Y.Z tag -> X.Y.Z.0 (a real Windows-client release; `win-v*` is its own tag namespace,
|
# win-vX.Y.Z tag -> X.Y.Z.0 (a real Windows-client release; `win-v*` is its own tag namespace,
|
||||||
# kept off the host's `host-v*` and the Apple `v*` to avoid the
|
# kept off the host's `host-v*` and the Apple `v*` to avoid the
|
||||||
# version-shadow class of bug — see deb.yml).
|
# version-shadow class of bug — see deb.yml).
|
||||||
# main push / dispatch -> 0.2.<run_number>.0 (rolling; climbs monotonically by run number).
|
# main push / dispatch -> 0.2.<run_number>.0 (rolling; climbs monotonically by run number).
|
||||||
|
# Both arches share the version; artifacts are arch-suffixed (..._x64.msix / ..._arm64.msix).
|
||||||
#
|
#
|
||||||
# Signing (packaging/pack-msix.ps1): if the MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD Actions secrets
|
# Signing (packaging/pack-msix.ps1): if the MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD Actions secrets
|
||||||
# are set (a real or shared code-signing .pfx whose subject DN == Publisher), the package is signed
|
# are set (a real or shared code-signing .pfx whose subject DN == Publisher), the package is signed
|
||||||
@@ -41,17 +45,33 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
package:
|
package:
|
||||||
runs-on: windows-amd64
|
runs-on: windows-amd64
|
||||||
timeout-minutes: 60
|
timeout-minutes: 90
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- arch: x64
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
ffmpeg: C:\Users\Public\ffmpeg
|
||||||
|
td: C:\t
|
||||||
|
- arch: arm64
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
ffmpeg: C:\Users\Public\ffmpeg-arm64
|
||||||
|
td: C:\t-a64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Configure + version
|
- name: Configure + version
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
# windows-reactor's build.rs unwraps CARGO_WORKSPACE_DIR; CARGO_TARGET_DIR=C:\t dodges the
|
# windows-reactor's build.rs unwraps CARGO_WORKSPACE_DIR; CARGO_TARGET_DIR (per-arch, short)
|
||||||
# MAX_PATH wall in the CMake-from-source crates (see windows.yml). Both via GITHUB_ENV.
|
# dodges the MAX_PATH wall in the CMake-from-source crates (see windows.yml). FFMPEG_DIR
|
||||||
|
# selects the arch's import libs + is read by pack-msix.ps1 for the runtime DLLs. All via
|
||||||
|
# GITHUB_ENV.
|
||||||
"CARGO_WORKSPACE_DIR=$env:GITHUB_WORKSPACE" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
"CARGO_WORKSPACE_DIR=$env:GITHUB_WORKSPACE" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
"CARGO_TARGET_DIR=C:\t" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
"CARGO_TARGET_DIR=${{ matrix.td }}" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
|
"FFMPEG_DIR=${{ matrix.ffmpeg }}" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
|
rustup target add ${{ matrix.target }}
|
||||||
$parts = if ($env:GITHUB_REF -like 'refs/tags/win-v*') {
|
$parts = if ($env:GITHUB_REF -like 'refs/tags/win-v*') {
|
||||||
($env:GITHUB_REF_NAME -replace '^win-v', '').Split('.')
|
($env:GITHUB_REF_NAME -replace '^win-v', '').Split('.')
|
||||||
} else {
|
} else {
|
||||||
@@ -60,11 +80,11 @@ jobs:
|
|||||||
while ($parts.Count -lt 4) { $parts += '0' }
|
while ($parts.Count -lt 4) { $parts += '0' }
|
||||||
$v = ($parts[0..3] -join '.')
|
$v = ($parts[0..3] -join '.')
|
||||||
"MSIX_VERSION=$v" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
"MSIX_VERSION=$v" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
Write-Output "MSIX version $v"
|
Write-Output "MSIX version $v arch ${{ matrix.arch }} target ${{ matrix.target }}"
|
||||||
|
|
||||||
- name: Build (release)
|
- name: Build (release)
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: cargo build --release -p punktfunk-client-windows
|
run: cargo build --release -p punktfunk-client-windows --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Pack + sign MSIX
|
- name: Pack + sign MSIX
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
@@ -73,7 +93,8 @@ jobs:
|
|||||||
MSIX_CERT_PASSWORD: ${{ secrets.MSIX_CERT_PASSWORD }}
|
MSIX_CERT_PASSWORD: ${{ secrets.MSIX_CERT_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
& clients/windows/packaging/pack-msix.ps1 `
|
& clients/windows/packaging/pack-msix.ps1 `
|
||||||
-Version $env:MSIX_VERSION -TargetDir C:\t\release -OutDir C:\t\msix
|
-Version $env:MSIX_VERSION -Arch ${{ matrix.arch }} `
|
||||||
|
-TargetDir ${{ matrix.td }}\${{ matrix.target }}\release -OutDir ${{ matrix.td }}\msix
|
||||||
|
|
||||||
- name: Publish to Gitea generic registry
|
- name: Publish to Gitea generic registry
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
# Windows client CI — runs on the self-hosted Windows runner (home-windows-1, host mode; see
|
# Windows client CI — runs on the self-hosted Windows runner (home-windows-1, host mode; see
|
||||||
# scripts/ci/setup-windows-runner.ps1). Build + clippy + fmt + test the WinUI 3 client
|
# scripts/ci/setup-windows-runner.ps1). Build + clippy + fmt + test the WinUI 3 client
|
||||||
# (windows-reactor + D3D11/SwapChainPanel + WASAPI + SDL3) on x86_64-pc-windows-msvc.
|
# (windows-reactor + D3D11/SwapChainPanel + WASAPI + SDL3).
|
||||||
#
|
#
|
||||||
# The MSVC/WinUI/FFmpeg toolchain (cargo/rustup on ASCII paths, NASM, CMake, LLVM, FFmpeg,
|
# Two architectures from ONE x64 runner: x86_64-pc-windows-msvc natively and
|
||||||
# CARGO_HOME, CMAKE_POLICY_VERSION_MINIMUM, …) is baked into the runner's daemon env. Two
|
# aarch64-pc-windows-msvc by cross-compiling. The x64 MSVC toolset ships an ARM64 cross compiler
|
||||||
# per-checkout vars are set in a step:
|
# (VC\Tools\MSVC\<ver>\bin\Hostx64\arm64\cl.exe) and aarch64-pc-windows-msvc is a tier-2 Rust
|
||||||
|
# target with host tools, so no ARM64 runner is needed — the cc/cmake crates pick the ARM64
|
||||||
|
# compiler from the target triple (SDL3 + libopus build-from-source cross-compile fine). The one
|
||||||
|
# arch-specific external dep is FFmpeg's import libs: the runner keeps an x64 tree at
|
||||||
|
# C:\Users\Public\ffmpeg and an ARM64 tree at C:\Users\Public\ffmpeg-arm64 (both FFmpeg 7.x /
|
||||||
|
# avcodec-61); the matrix points FFMPEG_DIR at the right one. aarch64 can't *run* on the x64 host,
|
||||||
|
# so fmt + test run only for x64.
|
||||||
|
#
|
||||||
|
# The MSVC/WinUI/FFmpeg toolchain (cargo/rustup on ASCII paths, NASM, CMake, LLVM, the x64 FFmpeg,
|
||||||
|
# CARGO_HOME, CMAKE_POLICY_VERSION_MINIMUM, …) is baked into the runner's daemon env. Per-checkout
|
||||||
|
# / per-arch vars are set in a step:
|
||||||
# - CARGO_WORKSPACE_DIR windows-reactor's build.rs unwraps it + stages the Win App SDK
|
# - CARGO_WORKSPACE_DIR windows-reactor's build.rs unwraps it + stages the Win App SDK
|
||||||
# NuGets/winmd under it (from GITHUB_WORKSPACE).
|
# NuGets/winmd under it (from GITHUB_WORKSPACE).
|
||||||
# - CARGO_TARGET_DIR=C:\t the runner's host workdir is buried deep under
|
# - CARGO_TARGET_DIR=C:\t… the runner's host workdir is buried deep under
|
||||||
# C:\Windows\System32\config\systemprofile\.cache\act\<hash>\hostexecutor\,
|
# C:\Windows\System32\config\systemprofile\.cache\act\<hash>\hostexecutor\,
|
||||||
# so the default target\ path blows past Windows' MAX_PATH (260) inside the
|
# so the default target\ path blows past Windows' MAX_PATH (260) inside the
|
||||||
# CMake-from-source builds (audiopus_sys / SDL3) — MSBuild's tracker then
|
# CMake-from-source builds (audiopus_sys / SDL3) — MSBuild's tracker then
|
||||||
# can't create its .tlog (DirectoryNotFoundException -> MSB6003). A short
|
# can't create its .tlog (DirectoryNotFoundException -> MSB6003). A short
|
||||||
# root keeps every nested path well under the limit.
|
# root keeps every nested path well under the limit (per-arch so the two
|
||||||
|
# matrix legs don't share a target dir).
|
||||||
|
# - FFMPEG_DIR per-arch FFmpeg import libs (x64 vs arm64 tree).
|
||||||
#
|
#
|
||||||
# Steps use `shell: pwsh` (PowerShell 7) deliberately: Windows PowerShell 5.1's
|
# Steps use `shell: pwsh` (PowerShell 7) deliberately: Windows PowerShell 5.1's
|
||||||
# `Out-File -Encoding utf8` prepends a UTF-8 BOM that corrupts the first GITHUB_ENV line (the
|
# `Out-File -Encoding utf8` prepends a UTF-8 BOM that corrupts the first GITHUB_ENV line (the
|
||||||
@@ -41,7 +53,11 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-amd64
|
runs-on: windows-amd64
|
||||||
timeout-minutes: 60
|
timeout-minutes: 90
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [x86_64-pc-windows-msvc, aarch64-pc-windows-msvc]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -49,24 +65,31 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
"CARGO_WORKSPACE_DIR=$env:GITHUB_WORKSPACE" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
"CARGO_WORKSPACE_DIR=$env:GITHUB_WORKSPACE" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
"CARGO_TARGET_DIR=C:\t" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
# Per-arch short target root (dodges MAX_PATH; keeps the two legs from sharing target\).
|
||||||
|
$td = if ('${{ matrix.target }}' -eq 'aarch64-pc-windows-msvc') { 'C:\t-a64' } else { 'C:\t' }
|
||||||
|
"CARGO_TARGET_DIR=$td" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
|
# Per-arch FFmpeg import libs (the runner provisions both — setup-windows-runner.ps1).
|
||||||
|
$ff = if ('${{ matrix.target }}' -eq 'aarch64-pc-windows-msvc') { 'C:\Users\Public\ffmpeg-arm64' } else { 'C:\Users\Public\ffmpeg' }
|
||||||
|
"FFMPEG_DIR=$ff" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
|
rustup target add ${{ matrix.target }}
|
||||||
rustc --version
|
rustc --version
|
||||||
cargo --version
|
cargo --version
|
||||||
node --version
|
Write-Output "target ${{ matrix.target }} target-dir $td ffmpeg $ff"
|
||||||
Write-Output "workspace: $env:GITHUB_WORKSPACE"
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: cargo build -p punktfunk-client-windows
|
run: cargo build -p punktfunk-client-windows --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Clippy (-D warnings)
|
- name: Clippy (-D warnings)
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: cargo clippy -p punktfunk-client-windows --all-targets -- -D warnings
|
run: cargo clippy -p punktfunk-client-windows --all-targets --target ${{ matrix.target }} -- -D warnings
|
||||||
|
|
||||||
- name: Rustfmt check
|
- name: Rustfmt check
|
||||||
|
if: matrix.target == 'x86_64-pc-windows-msvc'
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: cargo fmt -p punktfunk-client-windows -- --check
|
run: cargo fmt -p punktfunk-client-windows -- --check
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
|
if: matrix.target == 'x86_64-pc-windows-msvc'
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: cargo test -p punktfunk-client-windows
|
run: cargo test -p punktfunk-client-windows --target ${{ matrix.target }}
|
||||||
|
|||||||
@@ -140,7 +140,11 @@ Low-latency desktop/game streaming stack, Linux-first, with a shared Rust protoc
|
|||||||
dev VM is headless/WARP; needs the RTX box.)** **Stream input** is Win32 low-level hooks (`WH_KEYBOARD_LL`/`WH_MOUSE_LL`) — reactor
|
dev VM is headless/WARP; needs the RTX box.)** **Stream input** is Win32 low-level hooks (`WH_KEYBOARD_LL`/`WH_MOUSE_LL`) — reactor
|
||||||
exposes no raw key/pointer events; native Windows VK + absolute mouse (client-rect Contain-fit) +
|
exposes no raw key/pointer events; native Windows VK + absolute mouse (client-rect Contain-fit) +
|
||||||
wheel, Ctrl+Alt+Shift+Q capture toggle. `--headless`/`--discover` keep CLI paths. Builds + clippy
|
wheel, Ctrl+Alt+Shift+Q capture toggle. `--headless`/`--discover` keep CLI paths. Builds + clippy
|
||||||
+ fmt green on `x86_64-pc-windows-msvc` (on the dev VM). **windows-reactor is unpublished** (git
|
+ fmt green on **`x86_64-pc-windows-msvc` and `aarch64-pc-windows-msvc`** — the latter
|
||||||
|
**cross-compiled off the one x64 runner** (no ARM64 runner; the x64 MSVC toolset's ARM64 cross
|
||||||
|
compiler + a per-arch `FFMPEG_DIR` ARM64 tree, SDL3/libopus build-from-source cross-compile
|
||||||
|
cleanly), and both ship as signed MSIX (`windows-msix.yml` matrix → `..._x64.msix`/`..._arm64.msix`,
|
||||||
|
verified: ARM64 binaries + manifest arch). **windows-reactor is unpublished** (git
|
||||||
dep pinned to commit `b4129fcc`; `windows` pinned to the SAME commit so `IDXGISwapChain1` unifies
|
dep pinned to commit `b4129fcc`; `windows` pinned to the SAME commit so `IDXGISwapChain1` unifies
|
||||||
with `set_swap_chain`); its `build.rs` downloads the Win App SDK NuGets + needs `CARGO_WORKSPACE_DIR`
|
with `set_swap_chain`); its `build.rs` downloads the Win App SDK NuGets + needs `CARGO_WORKSPACE_DIR`
|
||||||
set (in the VM build env; `/temp`+`/winmd` gitignored). Gotcha: `CARGO_HOME` must be an ASCII path
|
set (in the VM build env; `/temp`+`/winmd` gitignored). Gotcha: `CARGO_HOME` must be an ASCII path
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
Name="unom.Punktfunk"
|
Name="unom.Punktfunk"
|
||||||
Publisher="{PUBLISHER}"
|
Publisher="{PUBLISHER}"
|
||||||
Version="{VERSION}"
|
Version="{VERSION}"
|
||||||
ProcessorArchitecture="x64" />
|
ProcessorArchitecture="{ARCH}" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Punktfunk</DisplayName>
|
<DisplayName>Punktfunk</DisplayName>
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
# punktfunk Windows client — MSIX packaging
|
# punktfunk Windows client — MSIX packaging
|
||||||
|
|
||||||
The Windows client ships as a **signed MSIX** so Windows boxes get a real package (Start tile,
|
The Windows client ships as **signed MSIX** packages so Windows boxes get a real package (Start
|
||||||
clean install/uninstall) instead of a loose exe. CI builds + publishes it from
|
tile, clean install/uninstall) instead of a loose exe. CI builds + publishes them from
|
||||||
[`.gitea/workflows/windows-msix.yml`](../../../.gitea/workflows/windows-msix.yml) to Gitea's
|
[`.gitea/workflows/windows-msix.yml`](../../../.gitea/workflows/windows-msix.yml) to Gitea's
|
||||||
**generic** package registry (`https://git.unom.io/unom/-/packages`), on every `main` push that
|
**generic** package registry (`https://git.unom.io/unom/-/packages`), on every `main` push that
|
||||||
touches the client and on `win-v*` release tags.
|
touches the client and on `win-v*` release tags.
|
||||||
|
|
||||||
|
**Two architectures, one x64 runner.** Both `x64` and `arm64` packages are produced off the single
|
||||||
|
x64 Windows runner — `x86_64-pc-windows-msvc` builds natively, `aarch64-pc-windows-msvc` is
|
||||||
|
cross-compiled (the x64 MSVC toolset ships the ARM64 cross compiler; the matrix points `FFMPEG_DIR`
|
||||||
|
at the runner's ARM64 FFmpeg tree, `C:\Users\Public\ffmpeg-arm64`). Artifacts are arch-suffixed
|
||||||
|
(`..._x64.msix` / `..._arm64.msix`, each with its matching `.cer`); `pack-msix.ps1 -Arch x64|arm64`
|
||||||
|
stamps the manifest `ProcessorArchitecture` and names the output. See
|
||||||
|
[`windows.yml`](../../../.gitea/workflows/windows.yml) for the cross-build rationale.
|
||||||
|
|
||||||
## What's in the package
|
## What's in the package
|
||||||
|
|
||||||
`pack-msix.ps1` assembles a layout from a `cargo build --release` and runs `makeappx` + `signtool`:
|
`pack-msix.ps1` assembles a layout from a `cargo build --release` and runs `makeappx` + `signtool`:
|
||||||
@@ -47,8 +55,9 @@ trusted with no further prompt:
|
|||||||
```powershell
|
```powershell
|
||||||
# once per machine (elevated): trust the publisher
|
# once per machine (elevated): trust the publisher
|
||||||
Import-Certificate -FilePath .\punktfunk-codesign.cer -CertStoreLocation Cert:\LocalMachine\TrustedPeople
|
Import-Certificate -FilePath .\punktfunk-codesign.cer -CertStoreLocation Cert:\LocalMachine\TrustedPeople
|
||||||
# then install (and re-run for each upgrade — no re-trust needed)
|
# then install the package for your CPU (and re-run for each upgrade — no re-trust needed)
|
||||||
Add-AppxPackage -Path .\punktfunk-client-windows_<ver>_x64.msix
|
Add-AppxPackage -Path .\punktfunk-client-windows_<ver>_x64.msix # Intel/AMD
|
||||||
|
Add-AppxPackage -Path .\punktfunk-client-windows_<ver>_arm64.msix # ARM64 (Snapdragon, etc.)
|
||||||
```
|
```
|
||||||
|
|
||||||
The matching `.cer` is also published next to each `.msix` in the registry, so it's always at hand.
|
The matching `.cer` is also published next to each `.msix` in the registry, so it's always at hand.
|
||||||
@@ -70,9 +79,16 @@ it changes the package identity → a one-time reinstall).
|
|||||||
On the Windows runner / dev VM (MSVC + Windows SDK present), after a release build:
|
On the Windows runner / dev VM (MSVC + Windows SDK present), after a release build:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cargo build --release -p punktfunk-client-windows
|
# x64
|
||||||
|
cargo build --release -p punktfunk-client-windows --target x86_64-pc-windows-msvc
|
||||||
pwsh -File clients/windows/packaging/pack-msix.ps1 `
|
pwsh -File clients/windows/packaging/pack-msix.ps1 `
|
||||||
-Version 0.2.0.0 -TargetDir C:\t\release -OutDir C:\t\msix
|
-Version 0.2.0.0 -TargetDir C:\t\x86_64-pc-windows-msvc\release -OutDir C:\t\msix
|
||||||
|
|
||||||
|
# arm64 (cross-compiled; point FFMPEG_DIR at the ARM64 tree)
|
||||||
|
$env:FFMPEG_DIR = 'C:\Users\Public\ffmpeg-arm64'
|
||||||
|
cargo build --release -p punktfunk-client-windows --target aarch64-pc-windows-msvc
|
||||||
|
pwsh -File clients/windows/packaging/pack-msix.ps1 `
|
||||||
|
-Version 0.2.0.0 -Arch arm64 -TargetDir C:\t\aarch64-pc-windows-msvc\release -OutDir C:\t\msix
|
||||||
```
|
```
|
||||||
|
|
||||||
Validated end-to-end on the build VM (pack → sign → `Add-AppxPackage` → framework-dependency
|
Validated end-to-end on the build VM (pack → sign → `Add-AppxPackage` → framework-dependency
|
||||||
|
|||||||
@@ -18,12 +18,17 @@
|
|||||||
Run on the Windows runner (or the dev VM) with the MSVC/Windows SDK present.
|
Run on the Windows runner (or the dev VM) with the MSVC/Windows SDK present.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
pwsh -File pack-msix.ps1 -Version 0.2.137.0 -TargetDir C:\t\release -FfmpegBin C:\Users\Public\ffmpeg\bin -OutDir C:\t\msix
|
# x64 (default arch):
|
||||||
|
pwsh -File pack-msix.ps1 -Version 0.2.137.0 -TargetDir C:\t\x86_64-pc-windows-msvc\release -OutDir C:\t\msix
|
||||||
|
# arm64 (point -TargetDir + FFMPEG_DIR at the ARM64 build/tree):
|
||||||
|
$env:FFMPEG_DIR='C:\Users\Public\ffmpeg-arm64'
|
||||||
|
pwsh -File pack-msix.ps1 -Version 0.2.137.0 -Arch arm64 -TargetDir C:\t-a64\aarch64-pc-windows-msvc\release -OutDir C:\t-a64\msix
|
||||||
#>
|
#>
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)][string]$Version, # 4-part numeric, e.g. 0.2.137.0
|
[Parameter(Mandatory = $true)][string]$Version, # 4-part numeric, e.g. 0.2.137.0
|
||||||
[Parameter(Mandatory = $true)][string]$TargetDir, # cargo --release output dir (has the exe)
|
[Parameter(Mandatory = $true)][string]$TargetDir, # cargo --release output dir (has the exe)
|
||||||
|
[ValidateSet('x64', 'arm64')][string]$Arch = 'x64', # package ProcessorArchitecture + artifact suffix
|
||||||
[string]$FfmpegBin = $(if ($env:FFMPEG_DIR) { Join-Path $env:FFMPEG_DIR 'bin' } else { 'C:\Users\Public\ffmpeg\bin' }),
|
[string]$FfmpegBin = $(if ($env:FFMPEG_DIR) { Join-Path $env:FFMPEG_DIR 'bin' } else { 'C:\Users\Public\ffmpeg\bin' }),
|
||||||
[string]$OutDir = (Join-Path $TargetDir 'msix'),
|
[string]$OutDir = (Join-Path $TargetDir 'msix'),
|
||||||
[string]$Publisher = 'CN=unom', # MUST equal the signing cert subject DN
|
[string]$Publisher = 'CN=unom', # MUST equal the signing cert subject DN
|
||||||
@@ -79,8 +84,8 @@ $ff | ForEach-Object { Copy-Item $_.FullName (Join-Path $layout $_.Name) -Force
|
|||||||
# tile/store assets
|
# tile/store assets
|
||||||
Copy-Item (Join-Path $assets '*') (Join-Path $layout 'Assets') -Force
|
Copy-Item (Join-Path $assets '*') (Join-Path $layout 'Assets') -Force
|
||||||
|
|
||||||
# manifest with version + publisher substituted
|
# manifest with version + publisher + architecture substituted
|
||||||
$manifest = (Get-Content -Raw $manifestTemplate).Replace('{VERSION}', $Version).Replace('{PUBLISHER}', $Publisher)
|
$manifest = (Get-Content -Raw $manifestTemplate).Replace('{VERSION}', $Version).Replace('{PUBLISHER}', $Publisher).Replace('{ARCH}', $Arch)
|
||||||
Set-Content -Path (Join-Path $layout 'AppxManifest.xml') -Value $manifest -Encoding UTF8
|
Set-Content -Path (Join-Path $layout 'AppxManifest.xml') -Value $manifest -Encoding UTF8
|
||||||
|
|
||||||
Write-Host "layout assembled at $layout :"
|
Write-Host "layout assembled at $layout :"
|
||||||
@@ -88,13 +93,13 @@ Get-ChildItem $layout -Recurse -File | ForEach-Object { " $($_.FullName.Substri
|
|||||||
|
|
||||||
# --- pack ---
|
# --- pack ---
|
||||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||||
$msix = Join-Path $OutDir "punktfunk-client-windows_${Version}_x64.msix"
|
$msix = Join-Path $OutDir "punktfunk-client-windows_${Version}_${Arch}.msix"
|
||||||
& $makeappx pack /o /d $layout /p $msix
|
& $makeappx pack /o /d $layout /p $msix
|
||||||
if ($LASTEXITCODE -ne 0) { throw "makeappx pack failed ($LASTEXITCODE)" }
|
if ($LASTEXITCODE -ne 0) { throw "makeappx pack failed ($LASTEXITCODE)" }
|
||||||
|
|
||||||
# --- signing cert (supplied stable pfx OR ephemeral self-signed) ---
|
# --- signing cert (supplied stable pfx OR ephemeral self-signed) ---
|
||||||
$pfxPath = Join-Path $OutDir 'signing.pfx'
|
$pfxPath = Join-Path $OutDir 'signing.pfx'
|
||||||
$cerPath = Join-Path $OutDir "punktfunk-client-windows_${Version}_x64.cer"
|
$cerPath = Join-Path $OutDir "punktfunk-client-windows_${Version}_${Arch}.cer"
|
||||||
if ($PfxBase64) {
|
if ($PfxBase64) {
|
||||||
Write-Host "signing with supplied code-signing cert (MSIX_CERT_PFX_B64)"
|
Write-Host "signing with supplied code-signing cert (MSIX_CERT_PFX_B64)"
|
||||||
[IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($PfxBase64))
|
[IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($PfxBase64))
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ the gotchas. Read it top to bottom, then start at **Phase 1** (de-risk Reactor f
|
|||||||
## Status — WinUI 3 client landed (2026-06-15)
|
## Status — WinUI 3 client landed (2026-06-15)
|
||||||
|
|
||||||
The client is implemented in `clients/windows` (binary `punktfunk-client`) and is
|
The client is implemented in `clients/windows` (binary `punktfunk-client`) and is
|
||||||
**build + clippy + fmt green on `x86_64-pc-windows-msvc`** (built on the dev VM). It is the **WinUI 3**
|
**build + clippy + fmt green on `x86_64-pc-windows-msvc` and `aarch64-pc-windows-msvc`** (the ARM64
|
||||||
client this doc planned: native chrome (host list, settings, in-app SPAKE2 PIN pairing) + the video on
|
target cross-compiled off the one x64 runner — see `windows.yml`; signed MSIX for both arches via
|
||||||
a **`SwapChainPanel`**, all in pure Rust.
|
`windows-msix.yml`). It is the **WinUI 3** client this doc planned: native chrome (host list,
|
||||||
|
settings, in-app SPAKE2 PIN pairing) + the video on a **`SwapChainPanel`**, all in pure Rust.
|
||||||
|
|
||||||
- **Reactor is viable after all — it is what we use.** The locked decision held. windows-rs
|
- **Reactor is viable after all — it is what we use.** The locked decision held. windows-rs
|
||||||
[PR #4499](https://github.com/microsoft/windows-rs/pull/4499) (merged 2026-06-01) added a
|
[PR #4499](https://github.com/microsoft/windows-rs/pull/4499) (merged 2026-06-01) added a
|
||||||
|
|||||||
@@ -84,6 +84,41 @@ if (-not (Test-Path "C:\Users\Public\.rustup\settings.toml")) {
|
|||||||
robocopy "$env:USERPROFILE\.rustup" "C:\Users\Public\.rustup" /E /NFL /NDL /NJH /NJS /MT:16 | Out-Null
|
robocopy "$env:USERPROFILE\.rustup" "C:\Users\Public\.rustup" /E /NFL /NDL /NJH /NJS /MT:16 | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- ARM64 cross-compile support (windows.yml / windows-msix.yml build aarch64-pc-windows-msvc off
|
||||||
|
# this x64 box; no ARM64 runner needed). Two pieces beyond the x64 toolchain:
|
||||||
|
# 1. the rustup std for the target;
|
||||||
|
# 2. an ARM64 FFmpeg import-lib/DLL tree at C:\Users\Public\ffmpeg-arm64 (the workflow matrix
|
||||||
|
# points FFMPEG_DIR here for the aarch64 leg; the x64 tree stays at C:\Users\Public\ffmpeg).
|
||||||
|
# The x64 MSVC toolset already ships the ARM64 cross compiler — if
|
||||||
|
# VC\Tools\MSVC\<ver>\bin\Hostx64\arm64\cl.exe is missing, add the VS "MSVC v143+ ARM64/ARM64EC
|
||||||
|
# build tools" + "C++ ARM64 build tools" workload components (the cc/cmake crates need it to
|
||||||
|
# cross-build SDL3 + libopus).
|
||||||
|
$env:RUSTUP_HOME = "C:\Users\Public\.rustup"
|
||||||
|
$env:CARGO_HOME = "C:\Users\Public\.cargo"
|
||||||
|
$rustup = (Get-Command rustup -ErrorAction SilentlyContinue).Source
|
||||||
|
if (-not $rustup) { $rustup = "C:\Users\Public\.cargo\bin\rustup.exe" }
|
||||||
|
if (Test-Path $rustup) {
|
||||||
|
Write-Host "==> rustup target add aarch64-pc-windows-msvc"
|
||||||
|
& $rustup target add aarch64-pc-windows-msvc
|
||||||
|
} else { Write-Warning "rustup not found - install rustup then re-run (needed for the aarch64 target)." }
|
||||||
|
|
||||||
|
$ffArm = "C:\Users\Public\ffmpeg-arm64"
|
||||||
|
if (-not (Test-Path (Join-Path $ffArm 'lib\avcodec.lib'))) {
|
||||||
|
# BtbN winarm64 shared, FFmpeg 7.x (avcodec-61) to match the x64 tree's ABI. MSVC-linkable .lib
|
||||||
|
# import libs + headers + bin\*.dll — exactly what ffmpeg-sys-next + pack-msix.ps1 consume.
|
||||||
|
Write-Host "==> fetching ARM64 FFmpeg (BtbN winarm64 shared)"
|
||||||
|
$ffUrl = 'https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-winarm64-gpl-shared-7.1.zip'
|
||||||
|
$ffZip = "C:\Users\Public\ffmpeg-arm64.zip"
|
||||||
|
$ffTmp = "C:\Users\Public\ffmpeg-arm64-extract"
|
||||||
|
Invoke-WebRequest -Uri $ffUrl -OutFile $ffZip -UseBasicParsing
|
||||||
|
if (Test-Path $ffTmp) { Remove-Item -Recurse -Force $ffTmp }
|
||||||
|
Expand-Archive -Path $ffZip -DestinationPath $ffTmp -Force # BtbN zips have one top-level folder
|
||||||
|
$inner = Get-ChildItem $ffTmp -Directory | Select-Object -First 1
|
||||||
|
if (Test-Path $ffArm) { Remove-Item -Recurse -Force $ffArm }
|
||||||
|
Move-Item -Path $inner.FullName -Destination $ffArm
|
||||||
|
Remove-Item -Force $ffZip; Remove-Item -Recurse -Force $ffTmp -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
# Inno Setup (ISCC.exe) for the host installer build (windows-host.yml). pack-host-installer.ps1
|
# Inno Setup (ISCC.exe) for the host installer build (windows-host.yml). pack-host-installer.ps1
|
||||||
# locates it at its fixed Program Files path, so it need not be on PATH — just present.
|
# locates it at its fixed Program Files path, so it need not be on PATH — just present.
|
||||||
if (-not (Test-Path "C:\Program Files (x86)\Inno Setup 6\ISCC.exe")) {
|
if (-not (Test-Path "C:\Program Files (x86)\Inno Setup 6\ISCC.exe")) {
|
||||||
|
|||||||
Reference in New Issue
Block a user