Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

cargo-reaper is a Cargo plugin designed to streamline the development of REAPER extension plugins with Rust. It serves as a companion for the reaper-rs library, which provides Rust bindings and tools for creating REAPER plugins -- including a procedural macro that bootstraps your plugin as a native REAPER extension.

Motivation

Developing REAPER extension plugins requires intimate knowledge about REAPER and its behavior on each platform that it supports. This information is somewhat esoteric and not listed in the development docs, making extension plugin development a trial-and-error ordeal. cargo-reaper aims to simplify the learning curve by providing an easy-to-use, intuitive interface for initializing, building, testing and publishing REAPER extension plugins.

Throughout this book are references to REAPER specific terminology, most of which you may safely ignore since cargo-reaper handles it for you, however, if you wish to know more the Glossary section contains detailed documentation that may aid you in understanding how cargo-reaper works. It is recommended to have a once-over for anyone creating a non-trivial extension plugin.

Platform Support

Any platform which is both supported by Rust and REAPER should work.

Important: extension plugins built for Windows must be built with MSVC.

The following are the canonical sources of truth for REAPER platform support:

Which can then be cross referenced against the Rust target list:

Or via rustc from the terminal:

rustc --print target-list

Configuration File

cargo-reaper requires a configuration file in the project root that conforms to the following naming convention (in precedence order):

  • reaper.toml
  • .reaper.toml

This file is created automatically for projects initialized by cargo-reaper-new.

Declaring Extension Plugins

cargo-reaper expects a key-value pair mapping of reaper extension plugins, where the key is the finalized name of the plugin and the value is the path to a directory containing a cargo manifest. All other information cargo-reaper needs is gathered from the manifest file.

A minimal reaper.toml, for a single cargo package could look like the following:

[extension_plugins]
reaper_hello_world_extension = "./."

Important: REAPER requires that extension plugins be prefixed by reaper_, otherwise REAPER will not recognize it.

cargo-reaper will throw an error and refuse to compile if an extension plugin listed does not meet this condition.

Plugin Manifest

There are a few necessities for cargo-reaper to recognize an extension plugin that is declared in a configuration file.

  1. The cargo manifest must be a package.
  2. The package must include a library target.
  3. The library target must include a name field.
  4. The library target must include cdylib in the crate-type field.

The above should be true whether the project is a single package, workspace with multiple packages or workspace package.

Package Manifest

An example of a single package manifest and its corresponding configuration file.

# Cargo.toml
[package]
name = "my_package"
version = "0.1.0"
edition = "2024"

[lib]
name = "my_extension_plugin"
crate-type = ["cdylib"]
# reaper.toml
[extension_plugins]
reaper_my_plugin = "./."

Workspace Manifests

Examples of acceptable workspace patterns and their corresponding configuration files.

Workspace Manifest

An example of a virtual workspace that does not contain a package attribute, but consists of multiple members, each of which being a package manifest meeting the plugin manifest criteria.

# Cargo.toml
[workspace]
resolver = "2"
members = ["crates/*"]
# reaper.toml
[extension_plugins]
reaper_my_plugin_1 = "./crates/my_plugin_1"
reaper_my_plugin_2 = "./crates/my_plugin_2"

Workspace Package Manifest

An example of a workspace package manifest and its corresponding configuration file.

# Cargo.toml
[workspace]
resolver = "2"
members = ["crates/*"]

[package]
name = "my_workspace_package"
version = "0.1.0"
edition = "2024"

[lib]
name = "my_extension_plugin"
crate-type = ["cdylib"]
# reaper.toml
[extension_plugins]
reaper_my_plugin = "./."

cargo-reaper

NAME

cargo-reaper -- A Cargo plugin for developing REAPER extension plugins with Rust.

SYNOPSIS

cargo-reaper command

DESCRIPTION

cargo-reaper is a convenience wrapper around Cargo that adds a post-build hook to streamline REAPER extension development. It automatically renames the compiled plugin to include the required reaper_ prefix and symlinks it to REAPER’s UserPlugins directory.

By default, Cargo prefixes dynamic libraries with lib, which REAPER does not recognize. Manually renaming the plugin and keeping the UserPlugins directory up-to-date can be tedious -- cargo-reaper takes care of all that for you, across all supported platforms.

COMMANDS

Each command is documented in its own section:

cargo-reaper new

Scaffold a new plugin project.

cargo-reaper list

Print plugin information to stdout.

cargo-reaper build

Compile REAPER plugin(s).

cargo-reaper link

Manually symlink plugin(s) to REAPER's UserPlugins directory.

cargo-reaper run

Compile plugin(s) and launch REAPER.

cargo-reaper clean

Remove generated symlinks and artifacts.

help

Print help or the help of the given subcommand(s).

OPTIONS

-h
--help

Print help (see more with --help).

-V
--version

Print version information.

cargo-reaper-new

NAME

cargo-reaper-new -- Create a new REAPER extension plugin.

SYNOPSIS

cargo-reaper new path

DESCRIPTION

This command will create a new Cargo package in the given directory that is set up for use with cargo-reaper. This includes a simple template with a Cargo.toml manifest, sample source file, reaper.toml configuration file, and a .gitignore file.

OPTIONS

-h
--help

Print help information.

EXAMPLES

cargo reaper new reaper_my_plugin

Important: REAPER requires that extension plugins be prefixed by reaper_, otherwise REAPER will not recognize it.

The reaper_ prefix is added by default in the cargo-reaper configuration file that is generated by cargo-reaper-new, however, cargo-reaper will throw an error and refuse to compile if an extension plugin listed does not meet this condition.

cargo-reaper-list

NAME

cargo-reaper-list -- List all detected REAPER plugin packages in a cargo-reaper project.

SYNOPSIS

cargo-reaper list

DESCRIPTION

This command prints a list of available plugins and their version, description and author information in a human-readable format to the terminal via stdout.

OPTIONS

-h
--help

Print help information.

EXAMPLES

cargo reaper list

cargo-reaper-build

NAME

cargo-reaper-build -- Compile REAPER extension plugin(s).

SYNOPSIS

cargo-reaper build [options] [cargo_build_args]...

DESCRIPTION

Compiles, renames and symlinks REAPER extension plugins, forwarding trailing arguments to the cargo-build invocation.

The resulting target is renamed to its corresponding key specified in the cargo-reaper configuration file. Symlinks to plugins in REAPER's UserPlugins directory are managed automatically, unless specified otherwise. This ensures that a new plugin that is built with the release profile, does not fail to be symlinked if a symlink with the same name already exists for the debug profile.

If for whatever reason symlinking fails, and the build command is unable to remove a stale symlink, use cargo-reaper-clean.

OPTIONS

--no-symlink

Prevent symlinking extension plugin(s) to the UserPlugins directory.

-h
--help

Print help information.

EXAMPLES

  1. Build a package or workspace containing a REAPER extension plugin, and all of its dependencies.
cargo reaper build
  1. Build a package or workspace containing a REAPER extension plugin and all of its dependencies, but do not create symlinks to the UserPlugins directory.
cargo reaper build --no-symlink
  1. Build only the specified package in a workspace containing a REAPER extension plugin with optimizations for x86_64 Windows.
cargo reaper build -p reaper_my_plugin --lib --release --target x86_64-pc-windows-msvc
cargo reaper build -- -p reaper_my_plugin --lib --release --target x86_64-pc-windows-msvc

Note that arguments passed to the cargo-build invocation must be trailing. These may be passed directly, or as positional arguments.

cargo-reaper-link

NAME

cargo-reaper-link -- Manually symlink extension plugin(s) to REAPER's UserPlugins directory.

SYNOPSIS

cargo-reaper link [path]...

DESCRIPTION

Manually symlink one or more extension plugins to REAPER's UserPlugins directory.

This may be useful in circumstances where finer grain control is necessary between building and symlinking the plugin, for instance, in CI or when using build tools like Nix or Docker.

By default cargo-reaper-build will symlink extension plugins automatically, unless specified otherwise.

OPTIONS

-h
--help

Print help information.

EXAMPLES

  1. Create a symlink from an absolute path to a compiled REAPER extension plugin to REAPER's UserPlugins directory.
cargo reaper link /absolute/path/to/target/release/reaper_my_plugin.{so|dylib|dll}
  1. Create a symlink from a relative path to a compiled REAPER extension plugin to REAPER's UserPlugins directory, using shell scripting (Linux and MacOS only).
cargo reaper link $(realpath target/release/reaper_my_plugin.*)

REAPER extension plugins are dynamically linked libraries, which have differing extension names depending on their target platform. Below is a list of platforms and their corresponding extension names, though in most cases, a regex catchall will suffice (reaper_my_plugin.*).

  • Linux -- .so
  • Darwin (MacOS) -- .dylib
  • Windows -- .dll

cargo-reaper-run

NAME

cargo-reaper-run -- Run REAPER extension plugin(s).

SYNOPSIS

cargo-reaper run [options] [cargo_build_args]...

DESCRIPTION

Compile extension plugins and open REAPER.

This is effectively shorthand for running cargo-reaper-build then opening REAPER, which will make changes to your extension plugins take immediate effect.

cargo-reaper-run will attempt to use the REAPER binary executable on $PATH if it's available, otherwise falling back to the platform specific default global installation path. If for some reason the default installation is not working, please see the options below for manually specifying a path to a REAPER binary executable.

OPTIONS

-e path
--exec path

Override the REAPER executable file path.

-o path
--open path
--open-project path

Open a specific REAPER project file.

--no-build

Do not build plugin(s) before running REAPER.

-t duration
--timeout duration

The amount of time to wait before closing REAPER, in human-readable format (e.g. 10s, 2m, 1h).

--stdin stdio

Configuration for the child process’s standard input (stdin) handle.

--stdout stdio

Configuration for the child process’s standard output (stdout) handle.

--stderr stdio

Configuration for the child process’s standard error (stderr) handle.

-h
--help

Print help information.

ADDITIONAL LINUX OPTIONS

The following options require xserver to be configured and have Xvfb and xdotool installed. These options are intended to enable testing in headless environments and to make it easier to assert the state an extension plugin reaches.

--headless

Run REAPER in a headless environment.

-D display
--display display

The virtual display that should be used for the headless environment. Can also be passed with the DISPLAY environment variable, e.g. DISPLAY=:99.

-w title
--locate-window title

Locate a window based on its title and exit with status code 0 if found.

--keep-going

Continue until the specified timeout, even after a window is located.

EXAMPLES

  1. Build a package or workspace containing a REAPER extension plugin and all of its dependencies, and open REAPER.
cargo reaper run
  1. Build only the specified package in a workspace containing a REAPER extension plugin with optimizations for x86_64 Windows, and open REAPER.
cargo reaper run -p reaper_my_plugin --lib --release --target x86_64-pc-windows-msvc
cargo reaper run -- -p reaper_my_plugin --lib --release --target x86_64-pc-windows-msvc

Note that arguments passed to the cargo-build invocation must be trailing. These may be passed directly, or as positional arguments.

  1. Run REAPER in a headless environment through Xvfb on Linux, and attempt to locate a window.
DISPLAY=:99 cargo-reaper run --headless \ # open REAPER on Xvfb display 99
  --no-build \                            # but don't build any plugins
  --open /path/to/my_project.RPP \        # open a pre-saved project (maybe with state that affects how the plugin behaves)
  --locate-window "error: expected ..." \ # and locate an error window and exit successfully
  --timeout 15s \                         # but only run REAPER for a maximum of 15 seconds
  --keep-going \                          # and don't exit until the timeout is reached (even if the window is found)
  --stdout null \                         # do not print Xvfb or REAPER info to stdout
  --stderr null                           # do not print Xvfb or REAPER errors to stderr

TIP: The above assumes the extension plugin is already installed, skipping the build phase. This can be particularly useful since it doesn't require configuring a rust toolchain in order to build the plugin prior to testing it.

cargo-reaper-clean

NAME

cargo-reaper-clean -- Remove plugin(s) from the UserPlugins directory that cargo-reaper has generated in the past.

SYNOPSIS

cargo-reaper clean [options]

DESCRIPTION

Clean plugin symlinks from REAPER's UserPlugins directory, and optionally artifacts generated by cargo-reaper.

OPTIONS

-p key
--plugin key

Clean plugin(s) by key.

-n
--dry-run

Display what would be deleted without deleting anything.

-a
--remove-artifacts

Remove artifacts that cargo-reaper has generated in the past.

-h
--help

Print help information.

EXAMPLES

  1. Remove all plugin symlinks whose keys exist in the cargo-reaper configuration file.
cargo reaper clean
  1. Remove specific plugin symlinks by referencing its key in the cargo-reaper configuration file.
cargo reaper clean -p reaper_my_plugin
  1. Display cargo artifacts generated by cargo-reaper and symlinks for plugins whose keys exist in the cargo-reaper configuration file without removing them.
cargo reaper clean --remove-artifacts --dry-run

Glossary

Default Global Installation Path

This is the system-wide path where the REAPER binary executable is installed. This is different for each supported platform:

  • Linux:
    • A global default is not predictable since Linux does not have a canonical package manager. Instead, cargo-reaper uses the which crate's which::which_global function to determine its location.
  • Darwin (MacOS) -- /Applications/REAPER.app
  • Windows:
    • x86 (32bit) -- C:\Program Files (x86)\REAPER\reaper.exe
    • x86_64 (64bit) -- C:\Program Files\REAPER (x64)\reaper.exe
    • aarch64 (ARM) -- C:\Program Files\REAPER (ARM64)\reaper.exe

Extension Plugin

A C/C++ dynamically linked library that when placed in REAPER's UserPlugins directory, is loaded as part of the REAPER application thread on launch, adding additional functionality to the program.

Dynamically Linked Library

A compiled collection of code and data that is loaded into a program at runtime, rather than being statically included in the final executable during compilation.

REAPER extension plugins are dynamically linked libraries, which have differing extension names depending on their target platform:

  • Linux -- .so
  • Darwin (MacOS) -- .dylib
  • Windows -- .dll

User Plugins

The UserPlugins directory is the file system location created the first time REAPER is launched that the REAPER application thread loads extension plugins from. This is different for each supported platform:

  • Linux -- ~/.config/REAPER/UserPlugins
  • Darwin (MacOS) -- ~/Library/Application\ Support/REAPER/UserPlugins
  • Windows -- %APPDATA%\REAPER\UserPlugins

Plugin Manifest

A plugin manifest, in the context of cargo-reaper, is the same as a Cargo manifest, which contains a package with a library target of crate-type cdylib.

See the Plugin Manifest section for detailed information on configuring a cargo-reaper plugin manifest.