FoxSDR 0.96.4 · OPEN BETA
← Back to FoxSDR

Source build · Apple silicon and Intel

Building on macOS

Nobody has finished this build yet. FoxSDR is developed on Windows and has a Linux CI job that passes on every commit. macOS has never been attempted. Everything on this page is derived from the actual build files and that working Linux recipe — but the build itself is unverified. Expect to hit something this page does not mention, and please tell us when you do.

The hard part is already done. The project builds and passes its full test suite on Linux in CI, so the non-Windows code path is genuinely exercised rather than theoretical. What follows is the difference between that path and a Mac.

What you need

Everything except SoapySDR and OpenSSL is vendored in third_party/: GLFW 3.4, Dear ImGui, PortAudio, pffft, nlohmann/json, tweetnacl and cpp-httplib all build from source in-tree. Vendored GLFW and PortAudio have real macOS support of their own — Cocoa and CoreAudio — so they should need nothing from you.

xcode-select --install                      # Apple Clang, needs C++20
brew install cmake ninja soapysdr openssl@3

CMake 3.20 or newer. The project is C++20 with CMAKE_CXX_STANDARD_REQUIRED ON, so an old Xcode fails at configure rather than mysteriously later.

SoapySDR is what talks to radios. FoxSDR has no device-specific code at all — every receiver arrives through SoapySDR::Device::enumerate(), so whatever SoapySDR modules you install are what it can use. brew install soapyrtlsdr, soapyairspy, soapyhackrf and so on, as suits your hardware. FoxSDR runs perfectly well with none of them, using the built-in signal generator or I/Q file playback.

Build

git clone https://github.com/wonderingStars/foxsdr.git
cd foxsdr
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
      -DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)"
cmake --build build -j"$(sysctl -n hw.ncpu)"

OPENSSL_ROOT_DIR is the one flag Linux does not need. macOS ships LibreSSL rather than OpenSSL, and Homebrew deliberately keeps openssl@3 off the default search path, so find_package(OpenSSL REQUIRED) will not find it on its own. OpenSSL is used for the password digest primitives and for the plugin catalogue's HTTPS client; Windows uses CNG and WinHTTP instead, which is why it is not a dependency there.

Then:

ctest --test-dir build --output-on-failure
./build/cascade --selftest

Run --selftest before concluding anything works. It drives the real DSP chain headlessly and asserts a known spectrum peak, so it catches a build that links cleanly, passes its unit tests, and computes wrong numbers.

One test opens a real window. On Linux CI that needs Xvfb; on macOS just run the tests from a normal logged-in GUI session and it should be fine.

The one patch you will almost certainly need

Two functions find the executable's own directory using Linux's /proc, which does not exist on macOS:

  • src/core/band_plan.cpp, in BandPlan::defaultDir()
  • src/core/plugin_host.cpp, around line 888

Both do:

const fs::path link = fs::read_symlink("/proc/self/exe", ec);

On macOS that fails, ec is set, the path stays empty, and both fall back to a path relative to the current directory. Nothing crashes — but the app will not find its band plans or its plugins unless you happen to launch it from the build directory. The fix is an __APPLE__ branch using _NSGetExecutablePath, and both files already have the #ifdef structure to hang it on:

#elif defined(__APPLE__)
    char buf[PATH_MAX];
    uint32_t size = sizeof(buf);
    if (_NSGetExecutablePath(buf, &size) == 0) {
        std::error_code rc;
        const fs::path resolved = fs::canonical(fs::path(buf), rc);
        exe = rc ? fs::path(buf) : resolved;
    }
#else
    // existing Linux /proc branch
#endif

with #include <mach-o/dyld.h> and #include <climits> alongside the other platform headers. _NSGetExecutablePath can return a path containing symlinks and .., which is why it is worth running through fs::canonical.

There is a test for the first one — test_band_plan asserts that defaultDir() ends in bandplans and is never a plain file — so you get a signal either way.

What will not work, and is not worth chasing

  • No crash reports. The crash handler is built on dbghelp and is Windows-only.
  • No usage reporting. The sender is WinHTTP-only, so a Mac build reports nothing — the same is already true of the Linux build.
  • The window keeps its title bar. src/gui/win_frame.cpp implements the frameless window through WM_NCCALCSIZE and WM_NCHITTEST; its whole body sits inside #ifdef _WIN32, so it compiles to nothing elsewhere and the window simply looks normal. Cosmetic only.

None of these stop the receiver working.

Band plans

0.81.0 ships nine band plans — world, the three ITU regions, and the UK, US, Canada, Japan and Australia. CMake stages them next to the built binary on every platform, so after a build you should have:

build/resources/bandplans/*.json

Pick your region in the app under Display → Region. If the picker is empty or the overlay never draws, that is the /proc/self/exe problem above rather than a missing file — check build/resources/bandplans/ exists first.

Reference: the Linux CI recipe

.github/workflows/build.yml builds on ubuntu-latest and is the closest thing to a known-good non-Windows build. Its dependency list translates like this:

LinuxmacOS
build-essentialXcode Command Line Tools
cmake ninja-buildbrew install cmake ninja
libsoapysdr-devbrew install soapysdr
libssl-devbrew install openssl@3 + OPENSSL_ROOT_DIR
libgl1-mesa-devin the macOS SDK already
libx11-dev, libwayland-dev, libxkbcommon-dev, …not needed — GLFW uses Cocoa
libasound2-devnot needed — PortAudio uses CoreAudio
xvfbnot needed in a GUI session

Configure and build commands are otherwise identical.

If you get it working

Tell us. A Mac build that somebody has actually run is worth more than this page, and the first person to do it turns "unverified" into a supported platform. The beta programme is where to say so, or write to [email protected].

Licence

FoxSDR is PolyForm Noncommercial — free for noncommercial use, and building it for yourself is squarely within that. Commercial use needs a separate licence. The vendored third-party components keep their own licences, listed in third_party/THIRD_PARTY.md.