Skip to content

CMake Reference

Source: tools/cmake/PulpUtils.cmake

pulp_add_plugin

Status: usable

Create plugin targets for multiple formats from a single declaration.

pulp_add_plugin(<target>
    FORMATS         <format-list>
    PLUGIN_NAME     <string>
    BUNDLE_ID       <string>
    MANUFACTURER    <string>
    VERSION         <string>
    CATEGORY        <Effect|Instrument|MidiEffect>
    ACCEPTS_MIDI
    PLUGIN_CODE     <4-char>
    MANUFACTURER_CODE <4-char>
    AAX_PRODUCT_CODE <4-char>
    AAX_NATIVE_CODE <4-char>
    SOURCES         <file-list>
    PROCESSOR_FACTORY <function-name>
    UI_SCRIPT       <path>
    CONTENT_CAPABILITIES <capability-list>
    CONTENT_KINDS   <presets|themes|samples|wavetables...>
    CONTENT_HOT_RELOAD_KINDS <presets|themes|samples|wavetables...>
    CONTENT_MANUAL_RESCAN_KINDS <presets|themes|samples|wavetables...>
)

Parameters

Parameter Required Default Description
FORMATS Yes -- Space-separated list: VST3, AU, AUv3, CLAP, LV2, AAX, Standalone
PLUGIN_NAME No <target> Display name for the plugin
BUNDLE_ID No -- Reverse-DNS identifier (e.g., com.company.plugin)
MANUFACTURER No "Unknown" Manufacturer name
VERSION No "1.0.0" Plugin version string
CATEGORY No "Effect" Plugin category: Effect, Instrument, or MidiEffect
ACCEPTS_MIDI No Off Mirrors PluginDescriptor::accepts_midi for package metadata. AU v2/AUv3 effects that accept MIDI need this so Pulp emits a MIDI-routable component type (aumf) instead of an audio-only effect (aufx). MIDI output is controlled by PluginDescriptor::produces_midi; there is no pulp_add_plugin(PRODUCES_MIDI) flag.
PLUGIN_CODE AU/AUv3 only -- 4-character AU plugin code
MANUFACTURER_CODE AU/AUv3/AAX only -- 4-character manufacturer code used by AU, AUv3, and AAX
AAX_PRODUCT_CODE AAX only -- 4-character stable AAX product identifier
AAX_NATIVE_CODE AAX only -- 4-character stable AAX Native identifier
SOURCES No -- Source files for the core object library
PROCESSOR_FACTORY No -- Factory function name (for generated entry points)
UI_SCRIPT No -- Path to a JavaScript UI entry file. Pulp applies it to created format targets via PULP_UI_SCRIPT_PATH; the Standalone macOS lane owns runtime validation for live JS/theme reload.
CONTENT_CAPABILITIES No -- Runtime content capabilities the plugin actually consumes, such as content.presets.v1. Use this when the plugin can load installed content packs. Must be paired with CONTENT_KINDS.
CONTENT_KINDS No -- Content kinds accepted by the plugin: presets, themes, samples, wavetables. This lets users and agents reject mismatched packs before install. Must be paired with CONTENT_CAPABILITIES.
CONTENT_HOT_RELOAD_KINDS No -- Accepted content kinds the plugin can refresh immediately after install/update. Each value must also appear in CONTENT_KINDS.
CONTENT_MANUAL_RESCAN_KINDS No -- Accepted content kinds that require an in-app rescan action but not a full restart. Each value must also appear in CONTENT_KINDS.

When CONTENT_CAPABILITIES and CONTENT_KINDS are present, pulp_add_plugin() generates a pulp.plugin-runtime.json resource containing the pulp.plugin-runtime.v1 manifest. Reload-policy fields are optional: accepted kinds listed in CONTENT_HOT_RELOAD_KINDS can update immediately, accepted kinds listed in CONTENT_MANUAL_RESCAN_KINDS should surface an in-app rescan action, and all other accepted kinds are restart-required in content preview/install UX. VST3/AU-style desktop bundles receive the manifest under Contents/Resources/; AUv3/iOS flat bundles receive it under Resources/; LV2 bundles receive it at the .lv2 bundle root; single file non-bundle formats receive a sibling <plugin-stem>.pulp.plugin-runtime.json sidecar. Validate the emitted artifact with ValidationHarness::validate_plugin_runtime_manifest(...).

Reviewed UI kits are consumed explicitly after pulp kit apply has generated cmake/pulp-kits.cmake. Include that file, declare the plugin, then attach the chosen kit script:

include(cmake/pulp-kits.cmake OPTIONAL)

pulp_add_plugin(MyPlugin
    FORMATS CLAP Standalone
    ...)

pulp_use_kit_ui(MyPlugin pulp_kit_dev_pulp_fixtures_basic_ui_kit)

pulp_use_kit_ui(<plugin-target> <kit-target> [SCRIPT <exported-path>] [TOKENS <exported-path>]) reads the reviewed kit target's PULP_UI_SCRIPTS, PULP_DESIGN_TOKENS, and PULP_ASSETS properties. The selected script is applied to existing format targets through PULP_UI_SCRIPT_PATH; the selected token/theme file is applied through PULP_UI_THEME_PATH; declared asset directories are applied through PULP_UI_ASSET_ROOTS. Scripted editors can then use __loadAssetSync__ with relative paths into those reviewed asset roots. Kits with multiple scripts or token files require the matching SCRIPT / TOKENS argument so the project makes the choice intentionally. The helper does not apply kits automatically and does not run package code.

pulp_add_plugin_bundle

Status: usable

Package MANY plugins into ONE binary per format — the multi-plugin counterpart to pulp_add_plugin. One .clap / .vst3 exposes N distinct plugins (Expert Sleepers Silent Way style). The single-plugin path is unchanged and remains the default; see PULP_PLUGIN_PACKAGING.

pulp_add_plugin_bundle(<target>
    FORMATS      CLAP VST3       # bundle-capable formats
    BUNDLE_NAME  <string>        # binary/bundle name (default <target>)
    BUNDLE_ID    <string>        # reverse-DNS id for the bundle
    VERSION      <string>        # default "1.0.0"
    MANUFACTURER <string>        # default "Unknown"
    SOURCES      <file-list>     # optional; union of the bundled plugins' sources
)

Each format's entry translation unit — clap_bundle_entry.cpp / vst3_bundle_entry.cpp in the target's source dir — uses the bundle macros (PULP_CLAP_BUNDLE_PLUGIN + PULP_CLAP_BUNDLE_ENTRY; PULP_VST3_BUNDLE_PLUGIN + PULP_VST3_FACTORY_BEGIN/_BUNDLE_CLASS/_FACTORY_END) to register N plugins into one binary. Per-plugin metadata (category, ids, and — for AU — component codes) lives in those macro calls, not in CMake args. The format functions automatically prefer a <fmt>_bundle_entry.cpp over the single-plugin <fmt>_entry.cpp.

Supported formats: CLAP and VST3 (each emits one entry symbol whose factory enumerates N plugins internally). AU/AUv3/AAX bundles additionally need a multi-component Info.plist and multi-symbol export and are not yet supported here — requesting one is a configure-time error rather than a silently-single-plugin binary.

See the plugin-bundles guide for a full walk-through and the examples/combined-bundle-demo project.

pulp_enable_midi_tuning_provider

Attach Pulp's provider-neutral optional microtuning wrappers to a plugin target. Use this after pulp_add_plugin() and after including any package CMake emitted by pulp add:

include(cmake/pulp-packages.cmake OPTIONAL)

pulp_add_plugin(MySynth
    FORMATS CLAP Standalone
    CATEGORY Instrument
    ACCEPTS_MIDI
    SOURCES src/PluginProcessor.cpp)

pulp_enable_midi_tuning_provider(MySynth MTS_ESP SCALA)

Supported providers are MTS_ESP and SCALA. MTS_ESP requires the mts_esp_client target created by pulp add mts-esp; SCALA requires the sst::tuning-library target created by pulp add sst-tuning-library. When a project uses both, MtsEspFallbackTuningProvider can prefer the active MTS-ESP session or prefer loaded local Scala tuning via MtsEspFallbackPolicy::PreferLocalTuning.

If the Pulp SDK was already built with the matching provider enabled, the helper only links pulp::midi and exposes the compile definition to the target. If an installed SDK was built without the optional provider, the helper compiles the small Pulp-owned wrapper source into your plugin target and links the package target. The third-party package remains opt-in either way.

Created Targets

Target Type Description
${target}_Core OBJECT or INTERFACE Shared processor code
${target}_VST3 MODULE VST3 bundle (.vst3)
${target}_AU MODULE AU v2 component (.component), macOS only
${target}_AUv3Framework SHARED FRAMEWORK AUv3 implementation framework, macOS only
${target}_AUv3 EXECUTABLE/BUNDLE AUv3 app extension (.appex), Apple platforms only
${target}_AUv3Host EXECUTABLE/BUNDLE macOS container app embedding the AUv3 extension
${target}_CLAP MODULE CLAP bundle (.clap)
${target}_LV2 MODULE LV2 bundle (.lv2)
${target}_AAX MODULE AAX Native bundle (.aaxplugin), macOS/Windows with developer-supplied SDK
${target}_Standalone EXECUTABLE Standalone app
pulp-install-${target} CUSTOM Copies built plugins to system folders

File Conventions

Each format target looks for an entry-point source file by convention:

Format Expected file
VST3 vst3_entry.cpp
CLAP clap_entry.cpp
AU v2 au_v2_entry.cpp
AUv3 au_v3_entry.cpp
LV2 lv2_entry.cpp
AAX aax_entry.cpp
Standalone main.cpp

Info.plist files can be provided per-plugin or generated from templates:

Format Custom plist Template
VST3 Info.plist.vst3 tools/cmake/PulpInfoPlist.vst3.in
AU Info.plist.au tools/cmake/PulpInfoPlist.au.in
AAX generated tools/cmake/PulpInfoPlist.aax.in

Metadata Resolution

pulp_add_plugin() is the public entry point for plugin metadata. It resolves per-format bundle metadata from the same declaration instead of asking projects to include format-specific helper files directly. AU v2 and AUv3 share the same component-type rules:

Declaration AU component type Host tag
CATEGORY Instrument aumu Synthesizer
CATEGORY MidiEffect aumi MIDI
CATEGORY Effect plus ACCEPTS_MIDI aumf Effects
CATEGORY Effect without ACCEPTS_MIDI aufx Effects

PLUGIN_CODE and MANUFACTURER_CODE must be exactly four characters for AU and AUv3 targets. VERSION is converted to the AU integer form from numeric major[.minor[.patch]] components with an optional - or + suffix, so 1.2.3-beta.1 keeps the same AU version number as 1.2.3. Generated plist and entitlement templates consume these resolved values; downstream projects should keep using pulp_add_plugin() rather than calling the internal format helpers.

Format Availability

Format targets are only created when the corresponding SDK is available:

  • VST3: requires PULP_HAS_VST3 (set when external/vst3sdk/ exists)
  • AU: requires APPLE and PULP_HAS_AUSDK (set when external/AudioUnitSDK/ exists)
  • AUv3: requires APPLE, PLUGIN_CODE, and MANUFACTURER_CODE; macOS creates a framework, .appex, and container .app, while iOS creates the .appex
  • CLAP: requires PULP_HAS_CLAP (fetched via FetchContent)
  • LV2: requires PULP_HAS_LV2 (set when LV2 headers are available)
  • AAX: requires APPLE or WIN32, PULP_ENABLE_AAX=ON, and PULP_AAX_SDK_DIR pointing to a developer-supplied out-of-tree AAX SDK
  • Standalone: always available

On Linux and Ubuntu, requesting AAX in FORMATS is a configure-time error.

Install Target

pulp-install-${target} copies supported built bundles to platform-standard locations:

Platform VST3 CLAP AU AUv3 AAX
macOS ~/Library/Audio/Plug-Ins/VST3/ ~/Library/Audio/Plug-Ins/CLAP/ ~/Library/Audio/Plug-Ins/Components/ ~/Applications/ plus PlugInKit registration /Library/Application Support/Avid/Audio/Plug-Ins/
Windows %COMMONPROGRAMFILES%/VST3/ %COMMONPROGRAMFILES%/CLAP/ -- -- %COMMONPROGRAMFILES%/Avid/Audio/Plug-Ins/
Linux ~/.vst3/ ~/.clap/ -- -- --

LV2 build output is created under build/LV2/<plugin>.lv2/; pulp-install-${target} does not currently copy LV2 bundles to a user plugin folder.

pulp_add_app

Status: partial

Create a standalone application target.

pulp_add_app(<target>
    APP_NAME    <string>
    BUNDLE_ID   <string>
    VERSION     <string>
)

Creates a basic executable target with compile definitions for the app name and bundle ID. Minimal functionality compared to pulp_add_plugin().

On Apple platforms the target is created as a bundle-capable executable so it can accept pulp_app_icon(...).

pulp_app_icon

Status: usable

Attach an icon source to an application or standalone target.

Targets build normally if you never call pulp_app_icon(...). The helper is an optional overlay, so removing the call cleanly removes the custom-icon behavior.

pulp_app_icon(<target>
    SOURCE       assets/app-icon.png
    MACOS        assets/app-icon-mac.png
    WINDOWS      assets/app-icon-win.png
    IOS          assets/app-icon-ios.png
    ANDROID      assets/app-icon-android.png
    LINUX        assets/app-icon-linux.png
    DEBUG_ICON   assets/app-icon-debug.png
    RELEASE_ICON assets/app-icon-release.png
)

Parameters

Parameter Required Default Description
SOURCE Yes, unless a platform override is present -- Default PNG used when a platform-specific override is absent; must be at least 1024×1024
MACOS No SOURCE macOS override
WINDOWS No SOURCE Windows override
IOS No SOURCE iOS override, recorded for downstream packaging
ANDROID No SOURCE Android override
LINUX No SOURCE Linux override
DEBUG_ICON No -- Debug-build variant, used when the active configure/build type is Debug
RELEASE_ICON No -- Release-build variant

Current Platform Outputs

  • macOS: generates AppIcon.icns and bundles it into the app
  • Windows: generates an .ico plus .rc and links it into the executable
  • Android: writes launcher PNGs under android/app/src/main/res-generated/
  • Linux: records the selected PNG as the target's canonical app-icon asset via the PULP_LINUX_APP_ICON target property
  • iOS: records the selected PNG as PULP_IOS_APP_ICON and warns that asset-catalog emission is not implemented

The selected source must be a PNG that is at least 1024×1024. Smaller inputs fail at configure time so a release doesn’t silently ship blurry assets.

Example:

pulp_add_plugin(MyPlugin
    FORMATS Standalone
    PLUGIN_NAME "MyPlugin"
    BUNDLE_ID "com.example.myplugin"
)

pulp_app_icon(MyPlugin_Standalone
    SOURCE assets/app-icon.png
)

pulp_add_binary_data

Status: usable

Embed binary assets as C++ arrays.

pulp_add_binary_data(MyResources
    SOURCES   logo.png preset.json font.ttf
    NAMESPACE myresources
)

Generates a static library with:

namespace myresources {
    extern const unsigned char logo_png[];
    extern const std::size_t logo_png_size;
}

Parameters

Parameter Required Default Description
SOURCES Yes -- Files to embed
NAMESPACE No <target> C++ namespace for the generated symbols

Implementation notes

The function delegates the byte-to-C-array encoding to the tools/cmake/scripts/encode_binary_data.py helper through add_custom_command(OUTPUT … DEPENDS …). As a result:

  • Encoding runs at build time, not configure time. Editing a source asset and re-running cmake --build regenerates the embedded .cpp automatically — no cmake -S/-B reconfigure required.
  • Encoding is fast: a 1 MB asset finishes in well under a second. The older in-CMake hex loop was effectively O(n²) and could pin a single reconfigure at 100 % CPU for 10–22 minutes.
  • Python (find_package(Python3 COMPONENTS Interpreter REQUIRED)) is the only added build-time dependency; Python is already required elsewhere in the Pulp build.

pulp_add_cargo_staticlib

Status: experimental

Build a Rust (Cargo) staticlib and expose it as an IMPORTED static-library target, for an opt-in native-component DSP core written in Rust (or any language that compiles to a C-ABI staticlib). Available via include(PulpCargoStaticlib). It requires a Rust toolchain (cargo + rustc, install from rustup.rs) and is intended for the opt-in lane gated behind PULP_BUILD_NATIVE_COMPONENT_RUST_TESTSOFF by default, so a default Pulp build never needs Rust.

include(PulpCargoStaticlib)

pulp_add_cargo_staticlib(
    NAME      MyGain_Core               # IMPORTED target name to create
    MANIFEST  ${CMAKE_CURRENT_SOURCE_DIR}/rust-core/Cargo.toml
    LIB_NAME  my_gain_core              # crate's [lib] name → libmy_gain_core.a
    PROFILE   release                   # optional: dev (default) | release
    FEATURES  simd                      # optional: forwarded as --features
)

# Link it into your component target; a direct symbol reference pulls the
# archive into the final module.
target_link_libraries(MyPlugin_Core PRIVATE MyGain_Core)

Parameters

Parameter Required Default Description
NAME Yes -- Name of the IMPORTED target to create
MANIFEST Yes -- Absolute path to the crate's Cargo.toml
LIB_NAME Yes -- The crate's [lib] name (the archive is lib<LIB_NAME>.a)
PROFILE No dev dev or release cargo profile
FEATURES No -- Cargo features, forwarded to --features

Implementation notes

  • The archive is built -C relocation-model=pic -C panic=abort (PIC so it links into Pulp's globally-PIC build; panic=abort so a Rust panic can never unwind across the extern "C" boundary — also pinned in the crate's Cargo.toml as belt-and-suspenders).
  • The custom command DEPENDS on a CONFIGURE_DEPENDS glob of the crate's src/*.rs plus its Cargo.toml, so editing Rust sources re-invokes cargo on the next build (cargo is itself incremental, so a no-op re-invoke is cheap). Without this, the OUTPUT rule would only fire when the archive was missing and could silently link a stale archive.
  • On Linux/Android the IMPORTED target's INTERFACE_LINK_LIBRARIES pulls in the system libraries the Rust std archive needs (Threads::Threads, ${CMAKE_DL_LIBS}, m); macOS resolves these through libSystem automatically.

pulp_faust_generate

Generate a checked-in C++ header from a FAUST .dsp source file. This helper is optional: when the faust compiler is not installed, the build keeps using the pre-generated file already in the source tree.

include(${PULP_ROOT}/tools/cmake/PulpFaust.cmake)

pulp_faust_generate(
    ${CMAKE_CURRENT_SOURCE_DIR}/generated_gain.hpp
    ${CMAKE_CURRENT_SOURCE_DIR}/gain.dsp
    FaustGainDsp
)

Parameters

Parameter Required Description
output_hpp Yes Header path to generate, normally under the source tree so it can be committed
input_dsp Yes FAUST .dsp source file
class_name Yes C++ class name passed to FAUST via -cn

pulp_add_faust_test

Add a Catch2 test target for a FAUST-backed processor.

pulp_add_faust_test(faust-gain-test
    test_faust_gain.cpp
    SOURCES faust_gain.hpp
)

Parameters

Parameter Required Description
target Yes Test executable target name
test_source Yes Main test source file
SOURCES No Additional source/header files needed by the test target

pulp_add_wam_plugin

Build a Pulp Processor into a WAMv2 (Web Audio Modules v2) WebAssembly AudioWorklet plugin. Experimental — targets a stereo, single-instance canary, not full WAM-host conformance.

Requires the Emscripten toolchain (configure with emcmake cmake ...); under any other toolchain the helper is a no-op, so it never breaks native builds.

include(${PULP_ROOT}/tools/cmake/PulpWam.cmake)

pulp_add_wam_plugin(PulpGain
    ENTRY    pulp_gain_wasm.cpp                 # required: the wam_* factory TU
    INCLUDES ${PULP_ROOT}/examples/pulp-gain    # plugin headers
    # SOURCES extra_dsp.cpp                      # optional: extra DSP sources
    # SINGLE_FILE                                # optional: BASE64-embed for AudioWorklet
)

Parameters

Parameter Required Description
ENTRY Yes The plugin's factory translation unit. It defines std::unique_ptr<pulp::format::Processor> pulp_wam_make_processor(); the shared wam_* C ABI lives in core/format/src/wasm/wam_entry.cpp.
SOURCES No Additional plugin DSP source files.
INCLUDES No Additional include directories (plugin headers).
SINGLE_FILE No Emit a BASE64-embedded ES-module factory for the AudioWorklet (sync compile, no fetch in worklet scope). Without it, a separate .wasm is emitted for the Node runner and export inspection.
NATIVE_EDITOR No Record in the web-build report that the plugin's native create_view() editor is replaced by generated controls in the headless web build.

Created targets

  • ${NAME}-wam — emits ${NAME}.js (+ ${NAME}.wasm unless SINGLE_FILE).

Notes

  • The helper compiles the headless DSP subset once into a shared pulp-wam-dsp object library and owns the wam_* EXPORTED_FUNCTIONS table and Emscripten link flags. It does not link the desktop pulp::format / pulp::state libraries, which publicly pull pulp::view/GPU and crypto/HTTP — none of which belongs in a headless browser DSP module.
  • choc headers are located via -DPULP_WAM_CHOC_INCLUDE=<dir containing choc/> (or a populated sibling build tree).