summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/codegen/dyngen.rs181
-rw-r--r--src/codegen/mod.rs80
-rw-r--r--src/lib.rs20
-rw-r--r--src/options.rs10
4 files changed, 288 insertions, 3 deletions
diff --git a/src/codegen/dyngen.rs b/src/codegen/dyngen.rs
new file mode 100644
index 00000000..aab6cac9
--- /dev/null
+++ b/src/codegen/dyngen.rs
@@ -0,0 +1,181 @@
+use crate::ir::function::Abi;
+use proc_macro2::Ident;
+
+/// Used to build the output tokens for dynamic bindings.
+pub struct DynamicItems {
+ /// Tracks the tokens that will appears inside the library struct -- e.g.:
+ /// ```ignore
+ /// struct Lib {
+ /// __library: ::libloading::Library,
+ /// x: Result<unsafe extern ..., ::libloading::Error>, // <- tracks these
+ /// ...
+ /// }
+ /// ```
+ struct_members: Vec<proc_macro2::TokenStream>,
+
+ /// Tracks the tokens that will appear inside the library struct's implementation, e.g.:
+ ///
+ /// ```ignore
+ /// impl Lib {
+ /// ...
+ /// pub unsafe fn foo(&self, ...) { // <- tracks these
+ /// ...
+ /// }
+ /// }
+ /// ```
+ struct_implementation: Vec<proc_macro2::TokenStream>,
+
+ /// Tracks the tokens that will appear inside the struct used for checking if a symbol is
+ /// usable, e.g.:
+ /// ```ignore
+ /// pub fn f(&self) -> Result<(), &'a ::libloading::Error> { // <- tracks these
+ /// self.__library.f.as_ref().map(|_| ())
+ /// }
+ /// ```
+ runtime_checks: Vec<proc_macro2::TokenStream>,
+
+ /// Tracks the initialization of the fields inside the `::new` constructor of the library
+ /// struct, e.g.:
+ /// ```ignore
+ /// impl Lib {
+ ///
+ /// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
+ /// where
+ /// P: AsRef<::std::ffi::OsStr>,
+ /// {
+ /// ...
+ /// let foo = __library.get(...) ...; // <- tracks these
+ /// ...
+ /// }
+ ///
+ /// ...
+ /// }
+ /// ```
+ constructor_inits: Vec<proc_macro2::TokenStream>,
+
+ /// Tracks the information that is passed to the library struct at the end of the `::new`
+ /// constructor, e.g.:
+ /// ```ignore
+ /// impl LibFoo {
+ /// pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
+ /// where
+ /// P: AsRef<::std::ffi::OsStr>,
+ /// {
+ /// ...
+ /// Ok(LibFoo {
+ /// __library: __library,
+ /// foo,
+ /// bar, // <- tracks these
+ /// ...
+ /// })
+ /// }
+ /// }
+ /// ```
+ init_fields: Vec<proc_macro2::TokenStream>,
+}
+
+impl Default for DynamicItems {
+ fn default() -> Self {
+ DynamicItems {
+ struct_members: vec![],
+ struct_implementation: vec![],
+ runtime_checks: vec![],
+ constructor_inits: vec![],
+ init_fields: vec![],
+ }
+ }
+}
+
+impl DynamicItems {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn get_tokens(
+ &self,
+ lib_ident: Ident,
+ check_struct_ident: Ident,
+ ) -> proc_macro2::TokenStream {
+ let struct_members = &self.struct_members;
+ let constructor_inits = &self.constructor_inits;
+ let init_fields = &self.init_fields;
+ let struct_implementation = &self.struct_implementation;
+ let runtime_checks = &self.runtime_checks;
+ quote! {
+ extern crate libloading;
+
+ pub struct #lib_ident {
+ __library: ::libloading::Library,
+ #(#struct_members)*
+ }
+
+ impl #lib_ident {
+ pub unsafe fn new<P>(
+ path: P
+ ) -> Result<Self, ::libloading::Error>
+ where P: AsRef<::std::ffi::OsStr> {
+ let __library = ::libloading::Library::new(path)?;
+ #( #constructor_inits )*
+ Ok(
+ #lib_ident {
+ __library: __library,
+ #( #init_fields ),*
+ }
+ )
+ }
+
+ pub fn can_call(&self) -> #check_struct_ident {
+ #check_struct_ident { __library: self }
+ }
+
+ #( #struct_implementation )*
+ }
+
+ pub struct #check_struct_ident<'a> {
+ __library: &'a #lib_ident,
+ }
+
+ impl<'a> #check_struct_ident<'a> {
+ #( #runtime_checks )*
+ }
+ }
+ }
+
+ pub fn add_function(
+ &mut self,
+ ident: Ident,
+ abi: Abi,
+ args: Vec<proc_macro2::TokenStream>,
+ args_identifiers: Vec<proc_macro2::TokenStream>,
+ ret: proc_macro2::TokenStream,
+ ret_ty: proc_macro2::TokenStream,
+ ) {
+ assert_eq!(args.len(), args_identifiers.len());
+
+ self.struct_members.push(quote!{
+ #ident: Result<unsafe extern #abi fn ( #( #args ),* ) #ret, ::libloading::Error>,
+ });
+
+ self.struct_implementation.push(quote! {
+ pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty {
+ let sym = self.#ident.as_ref().expect("Expected function, got error.");
+ (sym)(#( #args_identifiers ),*)
+ }
+ });
+
+ self.runtime_checks.push(quote! {
+ pub fn #ident (&self) -> Result<(), &'a::libloading::Error> {
+ self.__library.#ident.as_ref().map(|_| ())
+ }
+ });
+
+ let ident_str = ident.to_string();
+ self.constructor_inits.push(quote! {
+ let #ident = __library.get(#ident_str.as_bytes()).map(|sym| *sym);
+ });
+
+ self.init_fields.push(quote! {
+ #ident
+ });
+ }
+}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 91acf5b0..a62979b0 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -1,3 +1,4 @@
+mod dyngen;
mod error;
mod helpers;
mod impl_debug;
@@ -10,6 +11,7 @@ pub(crate) mod bitfield_unit;
#[cfg(all(test, target_endian = "little"))]
mod bitfield_unit_tests;
+use self::dyngen::DynamicItems;
use self::helpers::attributes;
use self::struct_layout::StructLayoutTracker;
@@ -184,6 +186,7 @@ impl From<DerivableTraits> for Vec<&'static str> {
struct CodegenResult<'a> {
items: Vec<proc_macro2::TokenStream>,
+ dynamic_items: DynamicItems,
/// A monotonic counter used to add stable unique id's to stuff that doesn't
/// need to be referenced by anything.
@@ -234,6 +237,7 @@ impl<'a> CodegenResult<'a> {
fn new(codegen_id: &'a Cell<usize>) -> Self {
CodegenResult {
items: vec![],
+ dynamic_items: DynamicItems::new(),
saw_bindgen_union: false,
saw_incomplete_array: false,
saw_objc: false,
@@ -247,6 +251,10 @@ impl<'a> CodegenResult<'a> {
}
}
+ fn dynamic_items(&mut self) -> &mut DynamicItems {
+ &mut self.dynamic_items
+ }
+
fn saw_bindgen_union(&mut self) {
self.saw_bindgen_union = true;
}
@@ -3785,7 +3793,29 @@ impl CodeGenerator for Function {
pub fn #ident ( #( #args ),* ) #ret;
}
};
- result.push(tokens);
+
+ // If we're doing dynamic binding generation, add to the dynamic items.
+ if ctx.options().dynamic_library_name.is_some() &&
+ self.kind() == FunctionKind::Function
+ {
+ let args_identifiers =
+ utils::fnsig_argument_identifiers(ctx, signature);
+ let return_item = ctx.resolve_item(signature.return_type());
+ let ret_ty = match *return_item.kind().expect_type().kind() {
+ TypeKind::Void => quote! {()},
+ _ => return_item.to_rust_ty_or_opaque(ctx, &()),
+ };
+ result.dynamic_items().add_function(
+ ident,
+ abi,
+ args,
+ args_identifiers,
+ ret,
+ ret_ty,
+ );
+ } else {
+ result.push(tokens);
+ }
}
}
@@ -4075,11 +4105,28 @@ pub(crate) fn codegen(
&(),
);
+ if context.options().dynamic_library_name.is_some() {
+ let lib_ident = context.rust_ident(
+ context.options().dynamic_library_name.as_ref().unwrap(),
+ );
+ let check_struct_ident = context.rust_ident(
+ [
+ "Check",
+ context.options().dynamic_library_name.as_ref().unwrap(),
+ ]
+ .join(""),
+ );
+ let dynamic_items_tokens = result
+ .dynamic_items()
+ .get_tokens(lib_ident, check_struct_ident);
+ result.push(dynamic_items_tokens);
+ }
+
result.items
})
}
-mod utils {
+pub mod utils {
use super::{error, ToRustTyOrOpaque};
use crate::ir::context::BindgenContext;
use crate::ir::function::{Abi, FunctionSig};
@@ -4484,6 +4531,35 @@ mod utils {
args
}
+ pub fn fnsig_argument_identifiers(
+ ctx: &BindgenContext,
+ sig: &FunctionSig,
+ ) -> Vec<proc_macro2::TokenStream> {
+ let mut unnamed_arguments = 0;
+ let args = sig
+ .argument_types()
+ .iter()
+ .map(|&(ref name, _ty)| {
+ let arg_name = match *name {
+ Some(ref name) => ctx.rust_mangle(name).into_owned(),
+ None => {
+ unnamed_arguments += 1;
+ format!("arg{}", unnamed_arguments)
+ }
+ };
+
+ assert!(!arg_name.is_empty());
+ let arg_name = ctx.rust_ident(arg_name);
+
+ quote! {
+ #arg_name
+ }
+ })
+ .collect::<Vec<_>>();
+
+ args
+ }
+
pub fn fnsig_block(
ctx: &BindgenContext,
sig: &FunctionSig,
diff --git a/src/lib.rs b/src/lib.rs
index 08b93817..7aa87831 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -518,6 +518,12 @@ impl Builder {
output_vector.push(path.into());
}
+ if self.options.dynamic_library_name.is_some() {
+ let libname = self.options.dynamic_library_name.as_ref().unwrap();
+ output_vector.push("--dynamic-loading".into());
+ output_vector.push(libname.clone());
+ }
+
// Add clang arguments
output_vector.push("--".into());
@@ -1468,6 +1474,15 @@ impl Builder {
self.options.wasm_import_module_name = Some(import_name.into());
self
}
+
+ /// Specify the dynamic library name if we are generating bindings for a shared library.
+ pub fn dynamic_library_name<T: Into<String>>(
+ mut self,
+ dynamic_library_name: T,
+ ) -> Self {
+ self.options.dynamic_library_name = Some(dynamic_library_name.into());
+ self
+ }
}
/// Configuration options for generated bindings.
@@ -1745,6 +1760,10 @@ struct BindgenOptions {
/// Wasm import module name.
wasm_import_module_name: Option<String>,
+
+ /// The name of the dynamic library (if we are generating bindings for a shared library). If
+ /// this is None, no dynamic bindings are created.
+ dynamic_library_name: Option<String>,
}
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1877,6 +1896,7 @@ impl Default for BindgenOptions {
no_hash_types: Default::default(),
array_pointers_in_arguments: false,
wasm_import_module_name: None,
+ dynamic_library_name: None,
}
}
}
diff --git a/src/options.rs b/src/options.rs
index a850dbbb..8c85abae 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -471,7 +471,11 @@ where
.long("wasm-import-module-name")
.value_name("name")
.takes_value(true)
- .help("The name to be used in a #[link(wasm_import_module = ...)] statement")
+ .help("The name to be used in a #[link(wasm_import_module = ...)] statement"),
+ Arg::with_name("dynamic-loading")
+ .long("dynamic-loading")
+ .takes_value(true)
+ .help("Use dynamic loading mode with the given library name."),
]) // .args()
.get_matches_from(args);
@@ -873,6 +877,10 @@ where
}
}
+ if let Some(dynamic_library_name) = matches.value_of("dynamic-loading") {
+ builder = builder.dynamic_library_name(dynamic_library_name);
+ }
+
let verbose = matches.is_present("verbose");
Ok((builder, output, verbose))