To start off, use cargo to setup two projects; one for the library itself, another for the executable that uses it:
cargo new dynamiclib cargo new --bin dynamic cd dynamiclibAdd the following to dynamiclib's Cargo.toml:
[lib] crate-type = ["dylib"]Setting the crate-type to "dylib" means a dynamic library will be built (rather than an executable or static library). Save the following as src/lib.rs:
#[no_mangle] pub extern "C" fn test() -> u32 { 47 }And build the library by running:
cargo buildNext, we need to create the executable that loads and uses the library we just created. Add the following to dynamic/Cargo.toml:
[dependencies] dylib = "0.0.2"And save the following as dynamic/src/main.rs:
extern crate dylib; use dylib::DynamicLibrary; use std::path::Path; fn main() { // I'm on OSX, obviously match DynamicLibrary::open(Some(Path::new("libdynamiclib.dylib"))) { Ok(lib) => { let test = unsafe { let ptr = lib.symbol::I eventually came across this post that's about calling rust from Node.js, and it made me realise that dylib is just a wrapper around Foreign Function Interface (FFI) (as opposed to some special/magical functionality provided by the runtime or a core library). This is easily confirmed if you check the dylib source; it just uses libc to call dlopen and kin. That means that rather than using prepend_search_path to ensure the dynamic library can be found, you can set LD_LIBRARY_PATH to the output path of dynamiclib (e.g. $HOME/dynamiclib/target/release). Allowing you to build and run the executable with:("test").unwrap(); println!("Found it: {:?}", ptr); std::mem::transmute::<_, fn() -> u32>(ptr) }; println!("Got: {}", test()); }, Err(e) => println!("Failed: {}", e) } }
LD_LIBRARY_PATH=$HOME/dynamiclib/target/release crate runI'm admittedly a little bit disappointed that dynamic libraries are so painfully close to native interop preventing idiomatic implementations that transparently work like native code. Something a bit more like the magic in .Net- but that would bring the runtime nastiness that rust tries so hard to avoid.
It's worth noting that if you have no intention of re-loading the dynamic library and you simply want to link with it, I could have changed dynamic/src/main.rs to:
#[link(name="dynamiclib")] extern { fn test() -> u32; } fn main() { println!("Got: {}", unsafe { test() }); }Now dynamiclib needs to be in one of the locations that rust searches for libraries (e.g. dynamic/target/debug) at build time.
No comments:
Post a Comment