Skip to content

C Library

Get the C library one of two ways — a pre-built release tarball (no build) or building from source — then choose an integration method below. Either way you get the headers plus a shared (libdoppler.so) and a self-contained static (libdoppler.a) library.

Install from a release tarball

Every GitHub release ships a pre-built C library for Linux (x86_64, aarch64) and macOS (arm64) — no toolchain or build step.

One-liner via jbx

jbx get-doppler                          # extracts to $HOME/.local/doppler
jbx get-doppler --prefix /opt/doppler    # or a custom prefix
jbx get-doppler --version 0.33.1         # pin a specific release
jbx get-doppler --restore                # roll back to the prior install

Resolves the latest release, downloads the platform-appropriate tarball, and extracts it — the manual steps below, in one command. A previous install at the same prefix is moved aside first, restored automatically if the new one fails a sanity check, and restorable any time with --restore. Needs jbx (make install-deps bootstraps it, or by hand: . <(curl -sSL https://just-buildit.github.io/get-jb.sh)). Source: scripts/get-doppler.sh.

Or by hand:

# Resolve the latest release tag (or set VERSION=x.y.z to pin a specific one):
VERSION=$(curl -fsSL https://api.github.com/repos/doppler-dsp/doppler/releases/latest \
  | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
# Linux x86_64 — for Linux aarch64 swap the suffix for `linux-aarch64`,
# for macOS arm64 swap it for `macos-arm64`:
curl -L -o doppler.tar.gz \
  "https://github.com/doppler-dsp/doppler/releases/download/v${VERSION}/doppler-${VERSION}-linux-x86_64.tar.gz"
mkdir -p "$HOME/.local/doppler" && tar -xzf doppler.tar.gz -C "$HOME/.local/doppler"

Point your build at the extracted prefix — CMake via CMAKE_PREFIX_PATH, or pkg-config via PKG_CONFIG_PATH — then use the find_package or pkg-config method shown below:

cmake -B build -DCMAKE_PREFIX_PATH="$HOME/.local/doppler"     # for find_package(doppler)
export PKG_CONFIG_PATH="$HOME/.local/doppler/lib/pkgconfig"   # for pkg-config doppler

The core library is pure C and links -lm and -lpthread, both part of a POSIX toolchain, so there is no external runtime dependency to install. The optional stream component (libdoppler_stream, for the NATS wire layer) embeds the vendored nats.c statically, so it too needs no external runtime NATS client library — just a running nats-server to connect to. See the static vs. dynamic linking design notes for why static vendoring was chosen over a system client-library dependency.

Windows

Every release also carries doppler-<version>-windows-x86_64.zip, built with clang-cl against the MSVC runtime. Your own code must be compiled with clang-cl as well: doppler's headers use C99 _Complex, which MSVC's cl.exe does not implement, and they stop a cl.exe build with an #error saying so. From a Developer PowerShell for VS (x64), with the C++ Clang tools for Windows component installed:

$v = "X.Y.Z"   # the release to install
curl.exe -L -o doppler.zip `
  "https://github.com/doppler-dsp/doppler/releases/download/v$v/doppler-$v-windows-x86_64.zip"
Expand-Archive doppler.zip -DestinationPath "$HOME\doppler"
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang-cl `
  -DCMAKE_PREFIX_PATH="$HOME\doppler"

find_package(doppler) works exactly as below; pkg-config is not the Windows path. doppler::doppler links the DLL — put $HOME\doppler\bin on PATH, or copy doppler.dll beside your executable — and doppler::doppler-static links doppler_static.lib with no DLL at all. The NATS stream layer is not in the Windows build (#1364).

With vcpkg

The repository carries its own vcpkg port and a clang-cl triplet, under packaging/vcpkg/. The port is an overlay: it builds the checkout it lives in, so you get the version you cloned, and it is not in the upstream microsoft/vcpkg registry — whose default x64-windows triplet compiles with cl.exe and therefore cannot build doppler.

$v = "X.Y.Z"   # the release to install
git clone --depth 1 --branch "v$v" https://github.com/doppler-dsp/doppler
vcpkg install doppler:x64-windows-clangcl `
  --overlay-ports=doppler/packaging/vcpkg/ports `
  --overlay-triplets=doppler/packaging/vcpkg/triplets
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang-cl `
  -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" `
  -DVCPKG_TARGET_TRIPLET=x64-windows-clangcl

vcpkg's toolchain finds the package and copies doppler.dll beside your executable, so there is no CMAKE_PREFIX_PATH and no PATH edit. vcpkg builds in a scrubbed environment and looks for clang-cl under %LLVMInstallDir%, then %ProgramFiles%\LLVM, then Visual Studio's Clang component. Three things to know:

  • Use a full vcpkg checkout, not Visual Studio's. A Developer PowerShell sets VCPKG_ROOT to the copy bundled with Visual Studio, which is manifest-only and answers vcpkg install <port> with "does not have a classic mode instance". Clone microsoft/vcpkg, run its bootstrap-vcpkg.bat, and point VCPKG_ROOT there. (doppler's own CI hit exactly this.)

  • Dynamic triplets only. The exported targets carry both linkages and refuse to load with one missing, so a static triplet (which must ship no DLL) is not expressible yet (#1405). doppler::doppler-static is still there to link against.

  • Rebuild after updating the clone. vcpkg's binary cache is keyed on the port files, not on the sources an overlay port points at — after a git pull, run vcpkg remove doppler first or pass --binarysource=clear.

The same port installs on Linux and macOS with the stock *-dynamic community triplets; CI exercises the Windows one on every pull request (make vcpkg-smoke).

System packages (.deb / .rpm)

Every release carries native packages for x86_64 and aarch64, attached to the GitHub Release. Download the ones for your format and install them with your package manager, which resolves the dependencies:

# Debian / Ubuntu
sudo apt install ./libdoppler-dsp0.*_*.deb ./libdoppler-dsp-dev_*.deb

# Fedora / RHEL / AlmaLinux
sudo dnf install ./libdoppler-dsp-0.*.rpm ./libdoppler-dsp-devel-*.rpm
Debian / Ubuntu Fedora / RHEL
shared libraries libdoppler-dsp<X.Y> libdoppler-dsp
headers, .a, CMake + pkg-config files libdoppler-dsp-dev libdoppler-dsp-devel
the wfmgen CLI doppler-dsp-tools doppler-dsp-tools

After that find_package(doppler) and pkg-config doppler resolve with no path of yours on the command line. Three things to know:

  • The name is doppler-dsp, as on PyPI — an unrelated doppler package (a secrets-manager CLI) is distributed from its vendor's own repository. The library itself is still libdoppler.so.
  • One package per architecture, for every distro at or above glibc 2.28 (Debian 10, Ubuntu 20.04, RHEL 8): they are built in the same manylinux_2_28 image as the tarballs, and CI installs them on AlmaLinux 8, the floor, on every pull request. The x86_64 build targets x86-64-v2 (SSE4.2), which is above the oldest CPUs those distros themselves still run on.
  • The Debian runtime package carries the ABI version in its name (libdoppler-dsp0.53 for the 0.53 series), so two minors install side by side and a program built against one keeps running after you install the next.

These are release assets, not an apt or yum repository: upgrading means downloading the next release's files.

System install

Install headers and libraries to a system prefix (default /usr/local) so find_package/pkg-config resolve with no per-project paths.

From a release tarball (no toolchain, no build) — point jbx get-doppler at the prefix. The sudo env "PATH=$PATH" form keeps jbx findable under sudo's restricted PATH:

sudo env "PATH=$PATH" jbx get-doppler --prefix /usr/local
sudo ldconfig

From a source build — only if you already built from source (a build/ tree exists):

cmake --install build
sudo ldconfig

Either way, verify the install is visible to your toolchain:

pkg-config --modversion doppler

CMake — find_package

The package provides two link targets — pick the one matching your linking policy:

find_package(doppler REQUIRED)

# shared: links -ldoppler; smallest binary
target_link_libraries(my_app PRIVATE doppler::doppler)

# static: pure C, self-contained — links -lm and -lpthread
target_link_libraries(my_app PRIVATE doppler::doppler-static)

# optional: the NATS stream layer (dp_pub_*/dp_sub_*, wfmgen --output nats://).
# Pure C (vendored nats.c, folded in). Link only if needed.
target_link_libraries(my_app PRIVATE doppler::stream-static)

A complete, buildable consumer that exercises both targets lives in example-projects/consumer/.

pkg-config

# shared
gcc -o app main.c $(pkg-config --cflags --libs doppler)

# static — pure C, self-contained: -lm and -lpthread
gcc -o app main.c $(pkg-config --cflags doppler) \
    "$(pkg-config --variable=libdir doppler)/libdoppler.a" -lm -lpthread

# the optional NATS stream layer has its own module — one name gives
# the whole line (shared; Requires: doppler)
gcc -o app main.c $(pkg-config --cflags --libs doppler_stream) -lm

# static + stream: name the archives explicitly
gcc -o app main.c $(pkg-config --cflags doppler) \
    "$(pkg-config --variable=libdir doppler)/libdoppler_stream.a" \
    "$(pkg-config --variable=libdir doppler)/libdoppler.a" \
    -lpthread -lm

Custom install prefix

cmake --install build --prefix ~/.local
export PKG_CONFIG_PATH=~/.local/lib/pkgconfig

Useful during development — no cmake --install required.

Shared library:

gcc -o app main.c \
    -Inative/inc \
    -Lbuild -ldoppler \
    -Wl,-rpath,"$(pwd)/build"

rpath on Linux

The -Wl,-rpath flag bakes the build path into the binary. If you move the binary or the build tree, set LD_LIBRARY_PATH or re-link.

Static library (no runtime .so dependency):

gcc -o app main.c \
    -Inative/inc \
    build/libdoppler.a \
    -lm

CMake — add_subdirectory:

add_subdirectory(path/to/doppler)
target_link_libraries(my_app PRIVATE doppler::doppler)

Embedding the wfmgen generator

The wfmgen composer CLI is archived into the library as a plain callable, so a C program can drive the full generator in-process — same flags, same output as the command line — without spawning a subprocess. Include wfm/wfmgen.h and call doppler_wfmgen(argc, argv):

#include <stddef.h>
#include "doppler/wfm/wfmgen.h"

int main(void)
{
    /* identical to: wfmgen --type qpsk --count 4096 --output out.cf32 */
    char *av[] = { "wfmgen", "--type", "qpsk", "--count", "4096",
                   "--output", "out.cf32", NULL };
    return doppler_wfmgen(7, av);   /* 0 on success; writes out.cf32 */
}

It is the exact code path the wfmgen binary runs (which is itself a one-line main shim over doppler_wfmgen), so the output is byte-identical. wfmgen lives in the pure-C core, so the file/raw/csv/BLUE/SigMF output paths link with just libdoppler.a -lm -lpthread:

gcc -o app app.c -I "$PREFIX/include" "$PREFIX/lib/libdoppler.a" \
    -lm -lpthread

The --output nats://… PUB-sink path additionally needs the optional stream component (it carries the vendored nats.c, pure C). Link libdoppler_stream.a alongside the core; nats.c is statically embedded, so there is still no runtime client-library dependency — just a running nats-server to connect to:

gcc -o app app.c -I "$PREFIX/include" \
    "$PREFIX/lib/libdoppler.a" "$PREFIX/lib/libdoppler_stream.a" \
    -lpthread -lm

Without the stream component, doppler_wfmgen() still builds and runs; only the nats:// path is unavailable (it reports a clear "requires the stream component" error via the weak wfm_stream_sink_* seam).

The shared library is even simpler — -ldoppler alone is sufficient.

POSIX only

doppler_wfmgen is built on the same surface as the wfmgen binary, including the NATS stream sink, which is not ported to Windows, so it is not available there. See Build from Source for what the Windows build includes.