Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
53 changes: 40 additions & 13 deletions .github/workflows/appimage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ jobs:
libgstreamer-plugins-bad1.0-0 \
libfuse2 file desktop-file-utils imagemagick \
python3-pip autoconf automake libtool libasound2-dev \
libfftw3-dev libhidapi-dev portaudio19-dev \
libfftw3-dev libudev-dev libhidapi-dev portaudio19-dev \
libsecret-1-dev

# glslc — the SPIR-V shader compiler whisper's Vulkan backend needs at
Expand Down Expand Up @@ -251,13 +251,17 @@ jobs:
# pure Qt-D-Bus backend talks to KDE Wallet / GNOME Keyring at runtime.
run: bash scripts/setup/setup-qtkeychain.sh

- name: Build pinned RTL dependencies (including float FFTW)
run: bash scripts/setup/setup-rtl-deps.sh

- name: Configure
run: |
# Without pipefail the step's exit status is tee's, so a cmake failure
# would look green. Set it in the script rather than via `shell: bash`
# (which also implies -eo pipefail) so the guard is self-contained and
# survives anyone editing the step's shell.
set -o pipefail
export PKG_CONFIG_PATH="$PWD/third_party/rtl-deps/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
# -g on top of Release's -O3: GCC's code generation does not depend
# on it, so the shipped code is unchanged. The DWARF is split off
# before packaging and attached to the release as its own archive
Expand All @@ -266,6 +270,7 @@ jobs:
-DCMAKE_BUILD_TYPE=Release \
"-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \
"-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \
-DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON \
-DCMAKE_INSTALL_PREFIX=/usr \
-DREQUIRE_SERIALPORT=ON \
-DREQUIRE_KEYCHAIN=ON \
Expand All @@ -275,7 +280,7 @@ jobs:
-DAETHER_GPU_SPECTRUM=${{ matrix.gpu_spectrum }} \
-DMQTT_TLS=OFF \
-DENABLE_NVIDIA_AFX=ON \
-DCMAKE_PREFIX_PATH="$QT_ROOT" 2>&1 | tee configure.log
-DCMAKE_PREFIX_PATH="$PWD/third_party/rtl-deps;$QT_ROOT" 2>&1 | tee configure.log

# AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake silently
# flips it back OFF if Qt6GuiPrivate can't be located, and CMakeLists.txt
Expand Down Expand Up @@ -321,6 +326,8 @@ jobs:
convert AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png \
-resize 256x256 AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png

mkdir -p AppDir/usr/share/aethersdr
cp -R third_party/rtl-deps/share/aethersdr-rtl-sources AppDir/usr/share/aethersdr/
# Bundle DeepFilterNet3 model for DFNR
if [ -f third_party/deepfilter/models/DeepFilterNet3_onnx.tar.gz ]; then
cp third_party/deepfilter/models/DeepFilterNet3_onnx.tar.gz \
Expand Down Expand Up @@ -389,7 +396,7 @@ jobs:
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch }}.AppImage
chmod +x linuxdeploy-${{ matrix.arch }}.AppImage linuxdeploy-plugin-qt-${{ matrix.arch }}.AppImage

- name: Build AppImage
- name: Deploy AppDir dependencies
env:
QT_SELECT: qt6
# EXTRA_QT_PLUGINS is the deprecated alias of EXTRA_QT_MODULES and
Expand All @@ -406,13 +413,6 @@ jobs:
# which the "Install to AppDir" step above does.
EXTRA_PLATFORM_PLUGINS: libqwayland.so
run: |
# Sanitize the ref for the output filename: a workflow_dispatch on a
# branch like "ci/appimage-qtkeychain" puts a '/' in github.ref_name,
# which breaks mksquashfs ("Could not create destination file"). Tag
# pushes (v26.6.x) are already slash-free; this mirrors the
# sanitization windows-installer.yml already applies.
SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}"
export LDAI_OUTPUT="AetherSDR-${SAFE_REF}-${{ matrix.arch }}.AppImage"
# linuxdeploy-plugin-qt locates the Qt to bundle via $QMAKE. This is
# the third place that used to hardcode the x86_64 ".../gcc_64" path;
# it now reuses the QT_ROOT exported by the aqt step, so there is one
Expand All @@ -423,13 +423,18 @@ jobs:
# libsherpa-onnx-c-api.so, and libonnxruntime.so (the app links sherpa's
# copy). Qt6 DBus, which the keychain backend needs at runtime, is pulled
# in transitively.
export LD_LIBRARY_PATH="${{ github.workspace }}/third_party/qtkeychain/lib:${{ github.workspace }}/third_party/onnxruntime/lib:${{ github.workspace }}/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}"
export LD_LIBRARY_PATH="${{ github.workspace }}/third_party/rtl-deps/lib:${{ github.workspace }}/third_party/qtkeychain/lib:${{ github.workspace }}/third_party/onnxruntime/lib:${{ github.workspace }}/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}"
# Official RTL builds ship the pinned libusb despite linuxdeploy's
# excludelist. Stage its matching SONAME before the dependency walk.
python3 tools/stage_rtl_appimage.py third_party/rtl-deps AppDir
./linuxdeploy-${{ matrix.arch }}.AppImage \
--appdir AppDir \
--plugin qt \
--desktop-file AppDir/usr/share/applications/AetherSDR.desktop \
--icon-file AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png \
--output appimage
--icon-file AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png

- name: Validate RTL AppDir runtime closure
run: python3 tools/check_rtl_package.py AppDir --platform linux

# Nothing else in the build would notice these plugins going missing: Qt
# dlopen()s them at runtime rather than linking them, so no dependency walk
Expand Down Expand Up @@ -487,6 +492,28 @@ jobs:
echo "Wayland integration plugin dirs:"
find "$plugins" -maxdepth 1 -type d -name 'wayland-*' | sort

- name: Build AppImage
run: |
# Branch refs contain slashes, which cannot appear in the output name.
SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}"
export LDAI_OUTPUT="AetherSDR-${SAFE_REF}-${{ matrix.arch }}.AppImage"
# linuxdeploy scans dependencies again when generating the image.
# Prefer the staged closure and retain the original build prefixes.
export LD_LIBRARY_PATH="$PWD/AppDir/usr/lib:$PWD/third_party/rtl-deps/lib:$PWD/third_party/qtkeychain/lib:$PWD/third_party/onnxruntime/lib:$PWD/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}"
./linuxdeploy-${{ matrix.arch }}.AppImage --appdir AppDir --output appimage

- name: Validate RTL runtime in the completed AppImage
run: |
set -euo pipefail
SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}"
image="$PWD/AetherSDR-${SAFE_REF}-${{ matrix.arch }}.AppImage"
audit_dir=$(mktemp -d)
trap 'rm -rf "$audit_dir"' EXIT
(cd "$audit_dir" && "$image" --appimage-extract > /dev/null)
python3 tools/check_rtl_package.py "$audit_dir/squashfs-root" --platform linux
cmp AppDir/usr/lib/libusb-1.0.so.0 "$audit_dir/squashfs-root/usr/lib/libusb-1.0.so.0"
sha256sum "$audit_dir/squashfs-root/usr/lib/libusb-1.0.so.0"

# The archive is only worth attaching if its build IDs are the ones the
# AppImage actually ships, so read them back from the packaged binaries.
# .tar.xz on purpose: sign-release.yml downloads and checksums *.zip, so
Expand Down
22 changes: 16 additions & 6 deletions .github/workflows/macos-dmg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ jobs:
# bakes in that nothing downstream can correct, so a 14.0 tree must
# never restore into a 12.0 build — that would silently recreate the
# bug this change exists to remove.
key: macos-deps-${{ matrix.label }}-deploy${{ matrix.deploy_target }}-${{ hashFiles('scripts/setup/setup-macos-deps.sh') }}
key: macos-deps-${{ matrix.label }}-deploy${{ matrix.deploy_target }}-${{ hashFiles('scripts/setup/setup-macos-deps.sh', 'scripts/setup/setup-rtl-deps.sh', 'scripts/setup/rtl-dependencies.json') }}

# ~8 min cold, then cached until the script or the target changes — the
# same deal third_party/qtkeychain already has. That cost buys the DMG a
Expand Down Expand Up @@ -371,6 +371,7 @@ jobs:
-DCMAKE_BUILD_TYPE=Release \
"-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \
"-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \
-DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OSX_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}" \
Expand Down Expand Up @@ -487,6 +488,9 @@ jobs:
"$DEPLOYQT" build/AetherSDR.app \
-verbose=1 \
-always-overwrite
mkdir -p build/AetherSDR.app/Contents/Resources
cp -R third_party/macos-deps/share/aethersdr-rtl-sources build/AetherSDR.app/Contents/Resources/
python3 tools/check_rtl_package.py build/AetherSDR.app --platform macos

# The artifact-side half of the pin. Everything upstream of here asserts
# what we *asked* for; this asserts what the DMG actually carries, which
Expand Down Expand Up @@ -667,6 +671,12 @@ jobs:

echo "DAX driver PKG v${APP_VERSION} built and signed"

- name: Name DMG artifact
run: |
# Branch dispatches contain slashes; preserve release tag filenames.
SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}"
echo "DMG_FILENAME=AetherSDR-${SAFE_REF}-macOS-${{ matrix.label }}.dmg" >> "$GITHUB_ENV"

- name: Create DMG
run: |
mkdir -p dmg-stage
Expand All @@ -692,7 +702,7 @@ jobs:
--hide-extension "AetherSDR.app" \
--app-drop-link 450 120 \
--icon "AetherSDR DAX Drivers.pkg" 450 290 \
"AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg" \
"$DMG_FILENAME" \
"dmg-stage/" \
|| true

Expand All @@ -705,7 +715,7 @@ jobs:
# no DMG on disk, and we surface that immediately instead of
# letting Sign DMG fail confusingly one step later. See the v26.6.1
# release-build chain for the exact symptom this guard catches.
DMG="AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg"
DMG="$DMG_FILENAME"
if [ ! -f "$DMG" ]; then
echo "::error::create-dmg did not produce $DMG"
exit 1
Expand All @@ -717,7 +727,7 @@ jobs:
run: |
codesign --force --timestamp \
--sign "Developer ID Application" \
AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg
"$DMG_FILENAME"

- name: Notarize DMG
env:
Expand All @@ -726,7 +736,7 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
xcrun notarytool submit \
AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg \
"$DMG_FILENAME" \
--apple-id "$APPLE_ID" \
--password "$APPLE_APP_PASSWORD" \
--team-id "$APPLE_TEAM_ID" \
Expand All @@ -744,7 +754,7 @@ jobs:
fi

xcrun stapler staple \
AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg
"$DMG_FILENAME"
echo "Notarization and stapling complete"

# Read the UUIDs back from the signed bundle the DMG was made from: the
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/static-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ jobs:
with:
python-version: "3.12"

- name: RTL release requirement combinations
run: python tools/test_require_rtl.py

# ── aetherd-migration ratchet (docs/aetherd-headless-engine-design.md §10)
# EB1/EB2 — the engine (src/core/ + src/models/) must never depend on
# the UI: no gui/ header includes, no QtWidgets.
Expand Down
21 changes: 19 additions & 2 deletions .github/workflows/windows-installer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,23 @@ jobs:
id: cache-fftw
with:
path: third_party/fftw3
key: fftw-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-fftw.ps1') }}
key: fftw-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-fftw.ps1', 'scripts/setup/rtl-dependencies.json') }}

- name: Setup FFTW3 float (NR4 dependency)
if: steps.cache-fftw.outputs.cache-hit != 'true'
run: .\scripts\setup\setup-fftw.ps1

- name: Cache RTL-SDR and libusb
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
id: cache-rtl
with:
path: third_party/rtl-deps
key: rtl-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-rtl-deps.ps1', 'scripts/setup/rtl-dependencies.json') }}

- name: Setup RTL-SDR and libusb
if: steps.cache-rtl.outputs.cache-hit != 'true'
run: .\scripts\setup\setup-rtl-deps.ps1

- name: Cache hidapi
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
id: cache-hidapi
Expand Down Expand Up @@ -279,7 +290,7 @@ jobs:
# native command), so a configure failure still fails this step —
# the reason the bash legs need `set -o pipefail` and this one does
# not.
cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log
cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log

# AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake flips it
# back OFF without failing when Qt6GuiPrivate can't be located, and
Expand Down Expand Up @@ -430,6 +441,12 @@ jobs:
Write-Host "Bundling ASR runtime DLL: $($_.Name)"
Copy-Item $_.FullName deploy\ -Force
}
Copy-Item third_party\rtl-deps\bin\*.dll deploy\
New-Item -ItemType Directory -Force deploy\licenses\rtl | Out-Null
Copy-Item third_party\rtl-deps\share\aethersdr-rtl-sources deploy\licenses\rtl\ -Recurse
Copy-Item third_party\fftw3\share\aethersdr-rtl-sources deploy\licenses\fftw -Recurse
python tools/check_rtl_package.py deploy --platform windows
if ($LASTEXITCODE -ne 0) { throw "RTL payload validation failed" }
# Vulkan loader for the whisper GPU backend. ggml-vulkan hard-links
# vulkan-1, so the DLL must ship or the exe fails to start even on
# CPU-only machines (the loader then enumerates zero devices and ggml
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,10 @@ third_party/qtkeychain-build/
# never be tracked or show up as untracked noise in the parent checkout.
.worktrees/

# Built pinned RTL dependency prefix and intermediate files
third_party/rtl-deps/
third_party/macos-deps/
.rtl-deps-build/

# macOS Finder metadata
.DS_Store
4 changes: 3 additions & 1 deletion BUILD-OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ alone. MSVC flags and `Release` builds are unaffected.
| Option | Default | Purpose and prerequisites |
|---|---|---|
| `ENABLE_DEEPFIST_EXPERIMENT` | OFF | Build the experimental DeepFist CW receive decoder. Requires ONNX Runtime at configure time (configuration fails without it) and a separate verified model bundle at runtime; see below. |
| `ENABLE_RTL` | ON | Build the experimental receive-only RTL-SDR USB backend when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. Missing either disables the backend. |
| `ENABLE_RTL` | ON | Build the experimental receive-only RTL-SDR USB backend when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. Missing either disables the backend unless `REQUIRE_RTL=ON`. |
| `ENABLE_HD_FM` | OFF | Build experimental embedded Digital FM using the pinned nrsc5 and FAAD HDC sources. Requires the RTL backend, librtlsdr and float FFTW; qualified build paths are Linux/GNU, macOS/AppleClang and Windows/MSVC x64 (with LLVM compiler-rt builtins). Official release workflows explicitly keep it OFF. See [the experimental guide](docs/rtl-hd-fm-experimental.md). |
| `AETHER_HL2_TX_TXA` | ON | Select WDSP's TXA chain for the Hermes-Lite 2 SSB transmit modulator. OFF builds the in-tree phasing modulator. This choice has no runtime toggle. |

**DeepFist:** enabling `-DENABLE_DEEPFIST_EXPERIMENT=ON` alone does not supply its
Expand Down Expand Up @@ -121,6 +122,7 @@ are useful for release builds that must include those capabilities.

| Option | Default | Fail configuration when… |
|---|---|---|
| `REQUIRE_RTL` | OFF | RTL is disabled, or `librtlsdr` or single-precision FFTW (`fftw3f`) is missing. Official release workflows set this ON together with `ENABLE_RTL=ON`. |
| `REQUIRE_SERIALPORT` | OFF | Qt SerialPort is missing. |
| `REQUIRE_KEYCHAIN` | OFF | QtKeychain is missing. |
| `REQUIRE_ASR_ONNX` | OFF | ONNX Runtime is missing for Silero VAD, speaker labeling and the signal classifier. |
Expand Down
Loading
Loading