Linkage
Savvy compiles the Rust code into a static library and then use it to generate a DLL for the R package. There's one tricky thing about static library. The Rust's official document about linkage says
Note that any dynamic dependencies that the static library may have (such as dependencies on system libraries, or dependencies on Rust libraries that are compiled as dynamic libraries) will have to be specified manually when linking that static library from somewhere.
What does this mean? If some of the dependency crate needs linking to a native
library, the necessary compiler flags are added by cargo
. But, after creating
the static library, cargo
's turn is over. It's you who have to tell the linker
the necessary flags because there's no automatic mechanism.
If some of the flags are missing, you'll see a "symbol not found" error. For example, this is what I got on macOS. Some dependency of my package uses the objc2 crate, and it needs to be linked against Apple's Objective-C frameworks.
unable to load shared object '.../foo.so':
dlopen(../foo.so, 0x0006): symbol not found in flat namespace '_NSAppKitVersionNumber'
Execution halted
So, how can we know the necessary flags? The official document provides a pro-tip!
The
--print=native-static-libs
flag may help with this.
You can add this option to src/Makevars.in
and src/Makevars.win.in
via
RUSTFLAGS
envvar. Please edit this line.
# Add flags if necessary
- RUSTFLAGS =
+ RUSTFLAGS = --print=native-static-libs
Then, you'll find this note in the installation log.
Compiling ahash v0.8.11
Compiling serde v1.0.210
Compiling zerocopy v0.7.35
...snip...
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: -framework CoreText -framework CoreGraphics -framework CoreFoundation -framework Foundation -lobjc -liconv -lSystem -lc -lm
Finished `dev` profile [unoptimized + debuginfo] target(s) in 19.17s
gcc -shared -L/usr/lib64/R/lib -Wl,-O1 -Wl,--sort-common -Wl,...
installing to /tmp/RtmpvQv8Ur/devtools_install_...
** checking absolute paths in shared objects and dynamic libraries
You can copy these flags to cargo build
. Please be aware that this differs on
platforms, so you probably need to run this command on CI, not on your local.
Also, since Linux and macOS requires different options, you need to tweak it in
the configure script.
For example, here's my setup on the vellogd package.
./configure
:
if [ "$(uname)" = "Darwin" ]; then
FEATURES=""
# result of --print=native-static-libs
ADDITIONAL_PKG_LIBS="-framework CoreText -framework CoreGraphics -framework CoreFoundation -framework Foundation -lobjc -liconv -lSystem -lc -lm"
else
FEATURES="--features use_winit"
fi
src/Makevars.in
:
PKG_LIBS = -L$(LIBDIR) -lvellogd @ADDITIONAL_PKG_LIBS@