Reputation: 8241
I would like to include a dynamic C library in Rust with FFI.
The library is actually also build with Rust, but exposes a C interface, so that it can be used from other languages, too. When I build the library (type: cdylib
) with cargo I get a .dylib
on MacOS and a .dll
as well as a .dll.lib
file on windows. These libraries also get different names, derived from the project name (libmy_lib.dylib
on MacOS and my_lib.dll
as well as my_lib.dll.lib
on Windows).
I would like to reference these files in a cross-platform way. Because currently I have to use
#[link(name = "my_lib.dll", kind = "dylib")]
on windows, whereas on MacOS I need to use
#[link(name = "my_lib", kind = "dylib")]
I have already tried to rename the my_lib.dll.lib
to my_lib.lib
, but I still get a Linker Error, saying
LINK : fatal error LNK1181: cannot open input file 'my_lib.lib'
How can I reference the files, so that I can use my code for Mac and Windows? If thats only possible with cfg_attr
tags I would also accept that. Ideally I would also like to get rid of the .lib
file for windows if possible.
Upvotes: 2
Views: 2457
Reputation: 3057
There are 2 ways to do this
You could use crate libloading
Example:
let lib = unsafe {
#[cfg(unix)]
let path = "mylib.so";
#[cfg(windows)]
let path = "mylib.dll";
libloading::Library::new(path).expect("Failed to load library")
};
let func: libloading::Symbol<unsafe extern fn() -> u32> = unsafe {
lib.get(b"my_func").expect("Failed to load function `my_func`")
};
// Can call func later while `lib` in scope
You need different attributes on extern
block depending on your target. This can be achieved using cfg_attr
attribute. It would select different attributes depending on target.
#[cfg_attr(windows, link(name = "my_lib.dll", kind = "dylib"))]
#[cfg_attr(unix, link(name = "my_lib.so", kind = "dylib"))]
extern "C" {
fn sum(a: u32, b: u32)->u64;
}
Upvotes: 2
Reputation: 8241
For what it's worth, I found a temporary solution for this now.
I used this pattern:
#[cfg(windows)]
#[link(name = "my_lib.dll", kind = "dylib")]
extern {
// Reference the exported functions
}
#[cfg(unix)]
#[link(name = "my_lib", kind = "dylib")]
extern {
// Reference the exported functions
}
I don't like it that much, because I had to define the very same extern{}
block twice, but it works and I could also extend this pattern to for example use #[cfg(target_os = "macos")]
if needed...
EDIT: Thanks to @Angelicos Phosphoros I improved the code a bit by using a macro like so:
/// Import native functions with the Rust FFI
macro_rules! import_native_functions {
() => {
// Reference the exported functions
};
}
#[cfg(windows)]
#[link(name = "my_lib.dll", kind = "dylib")]
extern {
import_native_functions!();
}
#[cfg(unix)]
#[link(name = "my_lib", kind = "dylib")]
extern {
import_native_functions!();
}
Upvotes: 1