Adventures in plugin development in Rust

Hi all! I’ve been working on this for a few weeks and wanted to share some progress.

It’s a plugin written mostly in the Rust language. Some amount of C++ code is unavoidable, since that’s what the Rack plugin API is built on top of. However, so far I’ve been able to get each module’s process() function implemented almost entirely in Rust, with not too much code boilerplate, and CPU utilization that I think should be equivalent to what you’d get with pure C++.

The way I’ve done this so far is the Rust code independently builds a static library with Cargo (libpotential.a) which the C++ build system links against. As a result, the build steps are a little clumsy because the stock Makefile system quite reasonably expects all of the source files to be C++, so we have to build the Rust code before running any of the Rack-specific make targets.

If anyone’s interested in the code, it’s available here: GitHub - dbonel/potential: VCV plugin code for Potential

I haven’t spent a lot of time on creating actual builds, so at the moment there aren’t any releases. I’ll get to that once I’ve cleared out a bit more of the backlog of ideas in my head. :slight_smile:

8 Likes

What are the advantages, what are the benefits of writing code in Rust over C++?

I’ll just answer this with my own personal opinion and hopefully avoid a big language tangent, of which you can infinite variations elsewhere online if you’re so inclined. :wink:

  1. I find the language much more pleasant to work in because of features like sum types (or algebraic data types), the borrow checker which keeps me from accidentally doing silly things, and the integrated build and unit test system Cargo provides.

  2. I already work with Rust in my day job, and the cognitive load of writing good C++ isn’t something I want to take on at the moment.

1 Like

Thank you for your explanation. So for me, not knowing Rust at all, it doesn’t make sense to take a closer look. But I’m generally interested in the modules you are going to develop in Rust and all that yet not unknown problems you may run into.

1 Like

Here’s my progress so far with building this on Windows. Installed Rust using rustup which allow me to config the x86_64-pc-windows-gnu toolchain. Edited libpotential\makefile to a x86_64-pc-windows-gnu release target.

From there:

$ make full
make -C libpotential libpotential.a
make[1]: Entering directory '/c/_Projects/VCVRack/V2/Rack/plugins/potential/libpotential'
env RUSTFLAGS="-C target-cpu=native" cargo build --release --target x86_64-pc-windows-gnu
   Compiling winapi-x86_64-pc-windows-gnu v0.4.0
   Compiling cc v1.0.79
   Compiling proc-macro2 v1.0.57
   Compiling unicode-ident v1.0.8
   Compiling winapi v0.3.9
   Compiling quote v1.0.27
   Compiling scratch v1.0.5
   Compiling syn v2.0.16
   Compiling link-cplusplus v1.0.8
   Compiling cxxbridge-flags v1.0.94
   Compiling unicode-width v0.1.10
   Compiling cfg-if v1.0.0
   Compiling getrandom v0.2.9
   Compiling cxx v1.0.94
   Compiling once_cell v1.17.1
   Compiling rand_core v0.6.4
   Compiling ppv-lite86 v0.2.17
   Compiling winapi-util v0.1.5
   Compiling rand_chacha v0.3.1
   Compiling termcolor v1.2.0
   Compiling rand v0.8.5
   Compiling codespan-reporting v0.11.1
   Compiling cxx-build v1.0.94
   Compiling cxxbridge-macro v1.0.94
   Compiling potential v0.1.0 (C:\_Projects\VCVRack\V2\Rack\plugins\potential\libpotential)
    Finished release [optimized] target(s) in 1m 39s
cp target/x86_64-pc-windows-gnu/release/libpotential.a .
cp target/x86_64-pc-windows-gnu/cxxbridge/potential/src/ffi.rs.h .
make[1]: Leaving directory '/c/_Projects/VCVRack/V2/Rack/plugins/potential/libpotential'
make -C libpotential potential.h
make[1]: Entering directory '/c/_Projects/VCVRack/V2/Rack/plugins/potential/libpotential'
make[1]: *** No rule to make target 'potential.h'.  Stop.
make[1]: Leaving directory '/c/_Projects/VCVRack/V2/Rack/plugins/potential/libpotential'
make: *** [Makefile:32: libpotential/potential.h] Error 2

Any thoughts?

1 Like

Thanks, I’ve just pushed a commit that should fix that.

1 Like

OK same as before, but now getting:

[snipped]

make[1]: Leaving directory '/c/_Projects/VCVRack/V2/Rack/plugins/potential/libpotential'
g++ -std=c++11 -Wsuggest-override  -I./libpotential -fPIC -I../../include -I../../dep/include -MMD -MP -g -O3 -funsafe-math-optimizations -fno-omit-frame-pointer -Wall -Wextra -Wno-unused-parameter -DARCH_X64 -march=nehalem -DARCH_WIN -D_USE_MATH_DEFINES -municode  -c -o build/src/breaker.cpp.o src/breaker.cpp
g++ -std=c++11 -Wsuggest-override  -I./libpotential -fPIC -I../../include -I../../dep/include -MMD -MP -g -O3 -funsafe-math-optimizations -fno-omit-frame-pointer -Wall -Wextra -Wno-unused-parameter -DARCH_X64 -march=nehalem -DARCH_WIN -D_USE_MATH_DEFINES -municode  -c -o build/src/mag_sign.cpp.o src/mag_sign.cpp
g++ -std=c++11 -Wsuggest-override  -I./libpotential -fPIC -I../../include -I../../dep/include -MMD -MP -g -O3 -funsafe-math-optimizations -fno-omit-frame-pointer -Wall -Wextra -Wno-unused-parameter -DARCH_X64 -march=nehalem -DARCH_WIN -D_USE_MATH_DEFINES -municode  -c -o build/src/plugin.cpp.o src/plugin.cpp
g++ -std=c++11 -Wsuggest-override  -I./libpotential -fPIC -I../../include -I../../dep/include -MMD -MP -g -O3 -funsafe-math-optimizations -fno-omit-frame-pointer -Wall -Wextra -Wno-unused-parameter -DARCH_X64 -march=nehalem -DARCH_WIN -D_USE_MATH_DEFINES -municode  -c -o build/src/polyshuffle.cpp.o src/polyshuffle.cpp
g++ -o plugin.dll build/src/breaker.cpp.o build/src/mag_sign.cpp.o build/src/plugin.cpp.o build/src/polyshuffle.cpp.o libpotential/libpotential.a -shared -L../.. -lRack -static-libstdc++
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: libpotential/libpotential.a(potential-2073c0ea51018a96.potential.
3f7144a8-cgu.4.rcgu.o): in function `std::sys::windows::handle::Handle::synchronous_write':
/rustc/90c541806f23a127002de5b4038be731ba1458ca/library\std\src\sys\windows/handle.rs:290: undefined reference to `NtWriteFile'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: /rustc/90c541806f23a127002de5b4038be731ba1458ca/library\std\src\s
ys\windows/handle.rs:318: undefined reference to `RtlNtStatusToDosError'
collect2.exe: error: ld returned 1 exit status
make: *** [../../compile.mk:77: plugin.dll] Error 1

Searching for the undefined references brings up stuff re: Windows drivers. I’m wondering whether I need to install MSVC build tools as well? Saying that I am going to install Visual Studio 2022 soon…

I don’t see an immediate fix for that one. Googling around, I see some past discussions (which you’ve probably also found) talking about problems with the x86_64-pc-windows-gnu platform and linker paths. I’ll have to dust off the old Windows box this week to see if I can find a workaround.

1 Like

Solved using the advice from this link: [Solved] Statically linking rust library yields undefined references - #6 by bjorn3 - help - The Rust Programming Language Forum

The following shows a list of all libraries you need to link against

$ cargo rustc -- --print native-static-libs
   Compiling cc v1.0.79
   Compiling proc-macro2 v1.0.57
   Compiling winapi-x86_64-pc-windows-gnu v0.4.0
   Compiling quote v1.0.27
   Compiling unicode-ident v1.0.8
   Compiling winapi v0.3.9
   Compiling scratch v1.0.5
   Compiling syn v2.0.16
   Compiling link-cplusplus v1.0.8
   Compiling unicode-width v0.1.10
   Compiling cfg-if v1.0.0
   Compiling cxxbridge-flags v1.0.94
   Compiling cxx v1.0.94
   Compiling getrandom v0.2.9
   Compiling once_cell v1.17.1
   Compiling rand_core v0.6.4
   Compiling winapi-util v0.1.5
   Compiling ppv-lite86 v0.2.17
   Compiling termcolor v1.2.0
   Compiling codespan-reporting v0.11.1
   Compiling rand_chacha v0.3.1
   Compiling cxx-build v1.0.94
   Compiling rand v0.8.5
   Compiling cxxbridge-macro v1.0.94
   Compiling potential v0.1.0 (C:\_Projects\VCVRack\V2\Rack\plugins\potential\libpotential)
note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.

note: native-static-libs: -lstdc++ -lbcrypt -ladvapi32 -lkernel32 -ladvapi32 -luserenv -lkernel32 -lkernel32 -lws2_32 -lbcrypt -lntdll

    Finished dev [unoptimized + debuginfo] target(s) in 1m 33s

This list (apart from stdc++ which is included in plugin.mk) need to added as LDFLAGS in the plugin makefile

I’ll do a Pull Request for this for you.

Another thing of note is that I had to comment out this line from libpotential\build.rs

.flag("-mmacosx-version-min=10.9")

For Windows builds that line would have to be conditionally ignored, I don’t how to do that in Rust.

1 Like

Thanks Steve! With your PR merged, I’ve also fixed up the build.rs script to detect the target OS before adding that flag.

1 Like