diff --git a/bindgen-build/Cargo.toml b/bindgen-build/Cargo.toml new file mode 100644 index 0000000000..dad273a2a3 --- /dev/null +++ b/bindgen-build/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bindgen-build" +description = "A package to link C/C++ inline functions" +version = "0.1.0" +authors = ["Flier Lu "] + +[dependencies] +failure = "0.1" +syn = { version = "0.15", features = ["full"] } +cc = "1.0" diff --git a/bindgen-build/src/lib.rs b/bindgen-build/src/lib.rs new file mode 100644 index 0000000000..0349fec450 --- /dev/null +++ b/bindgen-build/src/lib.rs @@ -0,0 +1,86 @@ +#[macro_use] +extern crate failure; +extern crate cc; +extern crate syn; + +use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; +use std::result::Result as StdResult; + +type Result = StdResult; + +pub fn build(path: P, name: S, config: F) -> Result<()> +where + P: AsRef, + S: AsRef, + F: FnOnce(&mut cc::Build), +{ + let mut gen = Generator::default(); + + // parse and extract C/C++ code + { + let mut f = File::open(path)?; + let mut content = String::new(); + f.read_to_string(&mut content)?; + let ast = syn::parse_file(&content)?; + syn::visit::visit_file(&mut gen, &ast); + } + + let mut build = cc::Build::new(); + build + .cpp(gen.saw_cpp) + .static_flag(true) + .cargo_metadata(true); + + // apply user configuration + config(&mut build); + + // generate the C/C++ wrapper file + { + let out_dir = env::var("OUT_DIR")?; + let out_file = Path::new(&out_dir).join(&format!( + "{}.{}", + name.as_ref(), + if gen.saw_cpp { "cpp" } else { "c" } + )); + build.file(&out_file); + + let mut f = File::create(out_file)?; + writeln!(f, "// generated by bindgen; DO NOT EDIT\n")?; + writeln!(f, "#pragma GCC diagnostic push")?; + writeln!(f, "#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"")?; + writeln!(f, "{}", gen.code)?; + writeln!(f, "#pragma GCC diagnostic pop")?; + } + + // build the C/C++ wrapper file + build + .try_compile(&format!("lib{}.a", name.as_ref())) + .map_err(|err| format_err!("fail to build, {:?}", err))?; + + Ok(()) +} + +#[derive(Debug, Default)] +struct Generator { + code: String, + saw_cpp: bool, +} + +impl<'ast> syn::visit::Visit<'ast> for Generator { + fn visit_item_macro(&mut self, item: &'ast syn::ItemMacro) { + if item.mac.path.is_ident("c") || item.mac.path.is_ident("cpp") { + self.code.push_str( + item.mac + .tts + .to_string() + .trim_start_matches('{') + .trim_end_matches('}'), + ); + self.code.push_str("\n"); + self.saw_cpp |= item.mac.path.is_ident("cpp"); + } + } +} diff --git a/src/clang.rs b/src/clang.rs index a31cba31e9..e64aacfd41 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -453,6 +453,15 @@ impl Cursor { unsafe { clang_Cursor_isFunctionInlined(self.x) != 0 } } + /// Determine the "thread-local storage (TLS) kind" of the declaration. + pub fn tls(&self) -> CXTLSKind { + if clang_getCursorTLSKind::is_loaded() { + unsafe { clang_getCursorTLSKind(self.x) } + } else { + CXTLS_None + } + } + /// Get the width of this cursor's referent bit field, or `None` if the /// referent is not a bit field. pub fn bit_width(&self) -> Option { @@ -550,6 +559,15 @@ impl Cursor { } } + /// Get the availability of the cursor's referent. + pub fn availability(&self) -> CXAvailabilityKind { + if clang_getCursorAvailability::is_loaded() { + unsafe { clang_getCursorAvailability(self.x) } + } else { + CXAvailability_Available + } + } + /// Given that this cursor's referent is a function, return cursors to its /// parameters. pub fn args(&self) -> Option> { @@ -877,6 +895,16 @@ impl Type { unsafe { clang_isConstQualifiedType(self.x) != 0 } } + /// Is this type volatile qualified? + pub fn is_volatile(&self) -> bool { + unsafe { clang_isVolatileQualifiedType(self.x) != 0 } + } + + /// Is this type is typedef? + pub fn is_typedef(&self) -> bool { + self.x.kind == CXType_Typedef + } + /// What is the size of this type? Paper over invalid types by returning `0` /// for them. pub fn size(&self) -> usize { @@ -1527,6 +1555,39 @@ pub fn type_to_str(x: CXTypeKind) -> String { unsafe { cxstring_into_string(clang_getTypeKindSpelling(x)) } } +/// Convert a linkage kind to a static string. +pub fn linkage_to_str(x: CXLinkageKind) -> &'static str { + match x { + CXLinkage_Invalid => "invalid", + CXLinkage_NoLinkage => "non-extern", + CXLinkage_Internal => "internal", + CXLinkage_UniqueExternal => "unique-external", + CXLinkage_External =>"external", + _ => unreachable!(), + } +} + +/// Convert a availability kind to a static string. +pub fn availability_to_str(x: CXAvailabilityKind) -> &'static str { + match x { + CXAvailability_Available => "available", + CXAvailability_Deprecated => "deprecated", + CXAvailability_NotAvailable => "not available", + CXAvailability_NotAccessible => "not accessible", + _ => unreachable!(), + } +} + +/// Convert a TLS kind into a static string. +pub fn tls_to_str(x: CXTLSKind) -> &'static str { + match x { + CXTLS_None => "none", + CXTLS_Dynamic => "dynamic", + CXTLS_Static => "static", + _ => unreachable!(), + } +} + /// Dump the Clang AST to stdout for debugging purposes. pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult { fn print_indent>(depth: isize, s: S) { @@ -1546,6 +1607,15 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult { depth, format!(" {}spelling = \"{}\"", prefix, c.spelling()), ); + print_indent( + depth, + format!(" {}linkage = {}", prefix, linkage_to_str(c.linkage())), + ); + print_indent( + depth, + format!(" {}availability = {}", prefix, availability_to_str(c.availability())), + ); + print_indent(depth, format!(" {}tls-kind = {}", prefix, tls_to_str(c.tls()))); print_indent(depth, format!(" {}location = {}", prefix, c.location())); print_indent( depth, diff --git a/src/codegen/helpers.rs b/src/codegen/helpers.rs index aea4b1fb42..7f6b4f623d 100644 --- a/src/codegen/helpers.rs +++ b/src/codegen/helpers.rs @@ -57,6 +57,18 @@ pub mod attributes { #[link_name = #name] } } + + pub fn deprecated(note: Option<&str>) -> proc_macro2::TokenStream { + if let Some(note) = note { + quote! { + #[deprecated(note = #note)] + } + } else { + quote! { + #[deprecated] + } + } + } } /// Generates a proper type for a field or type with a given `Layout`, that is, diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 9a7bf897e1..6c5b5774f2 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -26,7 +26,7 @@ use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDerivePartialEq, CanDeriveEq, CanDerive}; use ir::dot; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; -use ir::function::{Abi, Function, FunctionKind, FunctionSig, Linkage}; +use ir::function::{Abi, Function, FunctionKind, FunctionSig, Linkage, Availability}; use ir::int::IntKind; use ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath}; use ir::item_kind::ItemKind; @@ -107,6 +107,12 @@ struct CodegenResult<'a> { /// Whether a bitfield allocation unit has been seen at least once. saw_bitfield_unit: bool, + /// Whether a static inlined function have been seen at least once. + saw_static_inlined_function: bool, + + /// Whether a thread-local storage (TLS) variable have been seen at least once. + saw_tls_vars: bool, + items_seen: HashSet, /// The set of generated function/var names, needed because in C/C++ is /// legal to do something like: @@ -142,6 +148,8 @@ impl<'a> CodegenResult<'a> { saw_objc: false, saw_block: false, saw_bitfield_unit: false, + saw_static_inlined_function: false, + saw_tls_vars: false, codegen_id: codegen_id, items_seen: Default::default(), functions_seen: Default::default(), @@ -170,6 +178,14 @@ impl<'a> CodegenResult<'a> { self.saw_bitfield_unit = true; } + fn saw_static_inlined_function(&mut self) { + self.saw_static_inlined_function = true; + } + + fn saw_tls_vars(&mut self) { + self.saw_tls_vars = true; + } + fn seen>(&self, item: Id) -> bool { self.items_seen.contains(&item.into()) } @@ -217,6 +233,7 @@ impl<'a> CodegenResult<'a> { self.saw_block |= new.saw_block; self.saw_bitfield_unit |= new.saw_bitfield_unit; self.saw_bindgen_union |= new.saw_bindgen_union; + self.saw_static_inlined_function |= new.saw_static_inlined_function; new.items } @@ -412,6 +429,9 @@ impl CodeGenerator for Module { if result.saw_bitfield_unit { utils::prepend_bitfield_unit_type(&mut *result); } + if result.saw_static_inlined_function || result.saw_tls_vars { + utils::prepend_c_include(ctx, &mut *result); + } } }; @@ -492,7 +512,30 @@ impl CodeGenerator for Var { let ty = self.ty().to_rust_ty_or_opaque(ctx, &()); - if let Some(val) = self.val() { + if let Some(_) = self.tls() { + result.saw_tls_vars(); + + let ty_name = ctx.resolve_type(self.ty()) + .c_name(ctx) + .unwrap_or("__TYPE__".to_owned()) + .parse::() + .unwrap(); + let macro_name = if ctx.options().is_cpp() { "cpp" } else { "c" }; + let macro_name = macro_name.parse::().unwrap(); + let getter = ctx.rust_ident(&format!("__thread_{}", canonical_name)); + let setter = ctx.rust_ident(&format!("__thread_set_{}", canonical_name)); + + let tokens = quote! { + extern "C" { + pub fn #getter() -> #ty; + pub fn #setter(v: #ty); + } + + #macro_name ! {{ #ty_name #getter () { return #canonical_ident; } }} + #macro_name ! {{ void #setter (#ty_name v) { #canonical_ident = v; } }} + }; + result.push(tokens); + } else if let Some(val) = self.val() { match *val { VarType::Bool(val) => { result.push(quote! { @@ -3315,12 +3358,16 @@ impl CodeGenerator for Function { debug!("::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); - // We can't currently do anything with Internal functions so just + // We can't currently do anything with Internal non-inlined functions so just // avoid generating anything for them. - match self.linkage() { - Linkage::Internal => return, - Linkage::External => {} - } + let is_static_inlined_function = match self.linkage() { + Linkage::Internal if self.kind() == FunctionKind::Function && self.is_inlined() => { + result.saw_static_inlined_function(); + true + }, + Linkage::External => { false } + _ => { return } + }; // Pure virtual methods have no actual symbol, so we can't generate // something meaningful for them. @@ -3381,13 +3428,35 @@ impl CodeGenerator for Function { write!(&mut canonical_name, "{}", times_seen).unwrap(); } - if let Some(mangled) = mangled_name { + if self.availability() == Availability::Deprecated { + // TODO extract the deprecated message from the function attribute + attributes.push(attributes::deprecated(None)); + } + let cfunc_name = if let Some(mangled) = mangled_name { if canonical_name != mangled { attributes.push(attributes::link_name(mangled)); + Some(mangled.to_owned()) + } else { + None } } else if name != canonical_name { attributes.push(attributes::link_name(name)); - } + Some(name.to_owned()) + } else { + None + }.unwrap_or_else(|| { + if is_static_inlined_function { + let mut cfunc_name = format!("__inlined_{}", canonical_name); + let times_seen = result.overload_number(&cfunc_name); + if times_seen > 0 { + write!(&mut cfunc_name, "{}", times_seen).unwrap(); + } + attributes.push(attributes::link_name(&cfunc_name)); + cfunc_name + } else { + canonical_name.to_owned() + } + }); let abi = match signature.abi() { Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => { @@ -3409,12 +3478,38 @@ impl CodeGenerator for Function { abi => abi, }; - let ident = ctx.rust_ident(canonical_name); + let ident = ctx.rust_ident(&canonical_name); let tokens = quote!( extern #abi { #(#attributes)* pub fn #ident ( #( #args ),* ) #ret; }); result.push(tokens); + if is_static_inlined_function { + let ret_ty = ctx.resolve_type(signature.return_type()); + let ret_stmt = if ret_ty.is_void() { None } else { Some(quote!{ return }) }; + let ret_ty_name = ret_ty.c_name(ctx).unwrap_or("__TYPE__".to_owned()).parse::().unwrap(); + let arg_decls = signature.argument_types().iter().map(|&(ref name, ty)| { + let arg_ty = ctx.resolve_type(ty).c_name(ctx).unwrap_or("__TYPE__".to_owned()); + let arg_name = name.as_ref().map(|s| s.as_str()).unwrap_or("__NAME__"); + + format!("{} {}", arg_ty, arg_name).parse::().unwrap() + }); + let arg_names = signature.argument_types().iter().map(|&(ref name, _)| { + let arg_name = name.as_ref().map(|s| s.as_str()).unwrap(); + + arg_name.parse::().unwrap() + }); + let macro_name = if ctx.options().is_cpp() { "cpp" } else { "c" }; + let macro_name = macro_name.parse::().unwrap(); + let cfunc_name = cfunc_name.parse::().unwrap(); + + let tokens = quote!( #macro_name ! {{ + #ret_ty_name #cfunc_name ( #( #arg_decls ),* ) { + #ret_stmt #ident ( #( #arg_names ),* ); + } + }}); + result.push(tokens); + } } } @@ -3576,6 +3671,7 @@ mod utils { use ir::ty::TypeKind; use proc_macro2; use std::mem; + use std::path::Path; use std::str::FromStr; pub fn prepend_bitfield_unit_type(result: &mut Vec) { @@ -3587,6 +3683,27 @@ mod utils { result.extend(old_items); } + pub fn prepend_c_include( + ctx: &BindgenContext, + result: &mut Vec, + ) { + if let Some(ref header_file) = ctx.options().input_header() { + let macro_name = proc_macro2::TokenStream::from_str(if ctx.options().is_cpp() { "cpp" } else { "c" }).unwrap(); + let include = proc_macro2::TokenStream::from_str("#include").unwrap(); + let header_file = Path::new(header_file).file_name().unwrap().to_str().unwrap(); + + result.insert(0, quote! { + #[doc(hidden)] + macro_rules! #macro_name { + () => {}; + (#include $filename:tt $($rest:tt)*) => { #macro_name!{ $($rest)* } }; + ({ $($code:tt)* } $($rest:tt)*) => { #macro_name!{ $($rest)* } }; + } + }); + result.insert(1, quote!( #macro_name! { #include #header_file })) + } + } + pub fn prepend_objc_header( ctx: &BindgenContext, result: &mut Vec, diff --git a/src/ir/context.rs b/src/ir/context.rs index 063c79f0d3..99d7e1f6ba 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -1762,6 +1762,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" .ok(), sub_kind, false, + false, + false, ); let sub_id = self.next_item_id(); let sub_item = Item::new( @@ -1827,6 +1829,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" ty.fallible_layout().ok(), type_kind, ty.is_const(), + ty.is_volatile(), + ty.is_typedef(), ); let item = Item::new( with_id, @@ -1949,6 +1953,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" parent_id, ty, ty.is_const(), + ty.is_volatile(), + ty.is_typedef(), ) } @@ -1968,6 +1974,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" parent_id, ty, /* is_const = */ true, + /* is_volatile = */ false, + /* is_typedef = */ false, ) } @@ -1978,11 +1986,13 @@ If you encounter an error missing from this list, please file an issue or a PR!" parent_id: Option, ty: &clang::Type, is_const: bool, + is_volatile: bool, + is_typedef: bool, ) -> TypeId { let spelling = ty.spelling(); let layout = ty.fallible_layout().ok(); let type_kind = TypeKind::ResolvedTypeRef(wrapped_id); - let ty = Type::new(Some(spelling), layout, type_kind, is_const); + let ty = Type::new(Some(spelling), layout, type_kind, is_const, is_volatile, is_typedef); let item = Item::new( with_id, None, @@ -2059,8 +2069,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" let spelling = ty.spelling(); let is_const = ty.is_const(); + let is_volatile = ty.is_volatile(); + let is_typedef = ty.is_typedef(); let layout = ty.fallible_layout().ok(); - let ty = Type::new(Some(spelling), layout, type_kind, is_const); + let ty = Type::new(Some(spelling), layout, type_kind, is_const, is_volatile, is_typedef); let id = self.next_item_id(); let item = Item::new(id, None, None, self.root_module.into(), ItemKind::Type(ty)); diff --git a/src/ir/function.rs b/src/ir/function.rs index f851ad7224..f05337a4fd 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -61,7 +61,7 @@ impl FunctionKind { } /// The style of linkage -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Linkage { /// Externally visible and can be linked against External, @@ -69,6 +69,21 @@ pub enum Linkage { Internal } +/// Describes the availability of a particular entity, +/// which indicates whether the use of this entity will result in a warning or error +/// due to it being deprecated or unavailable. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Availability { + /// The entity is available. + Available, + /// The entity is available, but has been deprecated (and its use is not recommended). + Deprecated, + /// The entity is not available; any use of it will be an error. + NotAvailable, + /// The entity is available, but not accessible; any use of it will be an error. + NotAccessible +} + /// A function declaration, with a signature, arguments, and argument names. /// /// The argument names vector must be the same length as the ones in the @@ -92,6 +107,12 @@ pub struct Function { /// The linkage of the function. linkage: Linkage, + + /// The availability of the function. + availability: Availability, + + /// The function is inlined. + inlined: bool, } impl Function { @@ -102,7 +123,9 @@ impl Function { signature: TypeId, comment: Option, kind: FunctionKind, - linkage: Linkage + linkage: Linkage, + availability: Availability, + inlined: bool, ) -> Self { Function { name, @@ -111,6 +134,8 @@ impl Function { comment, kind, linkage, + availability, + inlined, } } @@ -139,6 +164,15 @@ impl Function { self.linkage } + /// Get this function's availability. + pub fn availability(&self) -> Availability { + self.availability + } + + /// Whether this function is inline. + pub fn is_inlined(&self) -> bool { + self.inlined + } } impl DotAttributes for Function { @@ -430,12 +464,11 @@ impl FunctionSig { }; let ptr = - Item::builtin_type(TypeKind::Pointer(class), false, ctx); + Item::builtin_type(TypeKind::Pointer(class), false, false, false, ctx); args.insert(0, (Some("this".into()), ptr)); } else if is_virtual { - let void = Item::builtin_type(TypeKind::Void, false, ctx); - let ptr = - Item::builtin_type(TypeKind::Pointer(void), false, ctx); + let void = Item::builtin_type(TypeKind::Void, false, false, false, ctx); + let ptr = Item::builtin_type(TypeKind::Pointer(void), false, false, false, ctx); args.insert(0, (Some("this".into()), ptr)); } } @@ -546,6 +579,7 @@ impl ClangSubItemParser for Function { { return Err(ParseError::Continue); } + let inlined = cursor.is_inlined_function(); let linkage = cursor.linkage(); let linkage = match linkage { @@ -554,6 +588,15 @@ impl ClangSubItemParser for Function { _ => return Err(ParseError::Continue) }; + let availability = cursor.availability(); + let availability = match availability { + CXAvailability_Available => Availability::Available, + CXAvailability_Deprecated => Availability::Deprecated, + CXAvailability_NotAvailable => Availability::NotAvailable, + CXAvailability_NotAccessible => Availability::NotAccessible, + _ => return Err(ParseError::Continue) + }; + // Grab the signature using Item::from_ty. let sig = Item::from_ty(&cursor.cur_type(), cursor, None, context)?; @@ -578,7 +621,7 @@ impl ClangSubItemParser for Function { let mangled_name = cursor_mangling(context, &cursor); let comment = cursor.raw_comment(); - let function = Self::new(name, mangled_name, sig, comment, kind, linkage); + let function = Self::new(name, mangled_name, sig, comment, kind, linkage, availability, inlined); Ok(ParseResult::New(function, Some(cursor))) } } diff --git a/src/ir/item.rs b/src/ir/item.rs index a54cbb827d..6bb66ed526 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -1234,6 +1234,8 @@ impl ClangItemParser for Item { fn builtin_type( kind: TypeKind, is_const: bool, + is_volatile: bool, + is_typedef: bool, ctx: &mut BindgenContext, ) -> TypeId { // Feel free to add more here, I'm just lazy. @@ -1245,7 +1247,7 @@ impl ClangItemParser for Item { _ => panic!("Unsupported builtin type"), } - let ty = Type::new(None, None, kind, is_const); + let ty = Type::new(None, None, kind, is_const, is_volatile, is_typedef); let id = ctx.next_item_id(); let module = ctx.root_module().into(); ctx.add_item( @@ -1447,6 +1449,8 @@ impl ClangItemParser for Item { debug!("New unresolved type reference: {:?}, {:?}", ty, location); let is_const = ty.is_const(); + let is_volatile = ty.is_volatile(); + let is_typedef = ty.is_typedef(); let kind = TypeKind::UnresolvedTypeRef(ty, location, parent_id); let current_module = ctx.current_module(); ctx.add_item( @@ -1455,7 +1459,7 @@ impl ClangItemParser for Item { None, None, parent_id.unwrap_or(current_module.into()), - ItemKind::Type(Type::new(None, None, kind, is_const)), + ItemKind::Type(Type::new(None, None, kind, is_const, is_volatile, is_typedef)), ), None, None, diff --git a/src/ir/layout.rs b/src/ir/layout.rs index c34da0e1de..b062d564d0 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -104,7 +104,9 @@ impl Opaque { let layout = Layout::new(ty.size(), ty.align()); let ty_kind = TypeKind::Opaque; let is_const = ty.is_const(); - Type::new(None, Some(layout), ty_kind, is_const) + let is_volatile = ty.is_volatile(); + let is_typedef = ty.is_typedef(); + Type::new(None, Some(layout), ty_kind, is_const, is_volatile, is_typedef) } /// Return the known rust type we should use to create a correctly-aligned diff --git a/src/ir/ty.rs b/src/ir/ty.rs index 922146ea8d..55479d05ef 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -1,6 +1,6 @@ //! Everything related to types in our intermediate representation. -use super::comp::CompInfo; +use super::comp::{CompInfo, CompKind}; use super::context::{BindgenContext, ItemId, TypeId}; use super::dot::DotAttributes; use super::enum_ty::Enum; @@ -32,6 +32,10 @@ pub struct Type { kind: TypeKind, /// Whether this type is const-qualified. is_const: bool, + /// Whether this type is volatile-qualified. + is_volatile: bool, + /// Whether this type is typedef. + is_typedef: bool, } /// The maximum number of items in an array for which Rust implements common @@ -66,12 +70,16 @@ impl Type { layout: Option, kind: TypeKind, is_const: bool, + is_volatile: bool, + is_typedef: bool, ) -> Self { Type { name, layout, kind, is_const, + is_volatile, + is_typedef, } } @@ -173,7 +181,7 @@ impl Type { /// Creates a new named type, with name `name`. pub fn named(name: String) -> Self { let name = if name.is_empty() { None } else { Some(name) }; - Self::new(name, None, TypeKind::TypeParam, false) + Self::new(name, None, TypeKind::TypeParam, false, false, false) } /// Is this a floating point type? @@ -209,11 +217,29 @@ impl Type { } } + /// Is this a void type? + pub fn is_void(&self) -> bool { + match self.kind { + TypeKind::Void => true, + _ => false, + } + } + /// Is this a `const` qualified type? pub fn is_const(&self) -> bool { self.is_const } + /// Is this a `volatile` qualified type? + pub fn is_volatile(&self) -> bool { + self.is_volatile + } + + /// Is this a typedef type? + pub fn is_typedef(&self) -> bool { + self.is_typedef + } + /// Is this a reference to another type? pub fn is_type_ref(&self) -> bool { match self.kind { @@ -312,6 +338,69 @@ impl Type { } } + pub fn c_name(&self, ctx: &BindgenContext) -> Option { + if self.is_typedef() { + self.name().map(|s| s.to_owned()) + } else { + match *self.kind() { + TypeKind::ResolvedTypeRef(inner) | + TypeKind::Alias(inner) | + TypeKind::BlockPointer(inner) | + TypeKind::TemplateAlias(inner, _) => { + ctx.resolve_type(inner).c_name(ctx) + }, + TypeKind::Pointer(inner) => { + ctx.resolve_type(inner).c_name(ctx).map(|inner_ty_name| { + let mut name = inner_ty_name.to_owned(); + + name.push_str(" *"); + + if self.is_const() { + name.push_str(" const"); + } + if self.is_volatile() { + name.push_str(" volatile"); + } + + name + }) + }, + TypeKind::Comp(ref comp) => { + self.name().map(|s| { + let mut name = String::new(); + + if self.is_const() { + name.push_str("const "); + } + match comp.kind() { + CompKind::Struct => { + name.push_str("struct "); + } + CompKind::Union => { + name.push_str("union "); + } + } + + name + s + }) + }, + TypeKind::Enum(_) => { + self.name().map(|s| { + let mut name = String::new(); + + if self.is_const() { + name.push_str("const "); + } + name.push_str("enum "); + + name + s + }) + }, + _ => self.name().map(|s| s.to_owned()), + } + } + } + /// See safe_canonical_type. pub fn canonical_type<'tr>( &'tr self, @@ -506,7 +595,7 @@ impl TypeKind { #[test] fn is_invalid_type_param_valid() { - let ty = Type::new(Some("foo".into()), None, TypeKind::TypeParam, false); + let ty = Type::new(Some("foo".into()), None, TypeKind::TypeParam, false, false, false); assert!(!ty.is_invalid_type_param()) } @@ -517,38 +606,40 @@ fn is_invalid_type_param_valid_underscore_and_numbers() { None, TypeKind::TypeParam, false, + false, + false, ); assert!(!ty.is_invalid_type_param()) } #[test] fn is_invalid_type_param_valid_unnamed_kind() { - let ty = Type::new(Some("foo".into()), None, TypeKind::Void, false); + let ty = Type::new(Some("foo".into()), None, TypeKind::Void, false, false, false); assert!(!ty.is_invalid_type_param()) } #[test] fn is_invalid_type_param_invalid_start() { - let ty = Type::new(Some("1foo".into()), None, TypeKind::TypeParam, false); + let ty = Type::new(Some("1foo".into()), None, TypeKind::TypeParam, false, false, false); assert!(ty.is_invalid_type_param()) } #[test] fn is_invalid_type_param_invalid_remaing() { - let ty = Type::new(Some("foo-".into()), None, TypeKind::TypeParam, false); + let ty = Type::new(Some("foo-".into()), None, TypeKind::TypeParam, false, false, false); assert!(ty.is_invalid_type_param()) } #[test] #[should_panic] fn is_invalid_type_param_unnamed() { - let ty = Type::new(None, None, TypeKind::TypeParam, false); + let ty = Type::new(None, None, TypeKind::TypeParam, false, false, false); assert!(ty.is_invalid_type_param()) } #[test] fn is_invalid_type_param_empty_name() { - let ty = Type::new(Some("".into()), None, TypeKind::TypeParam, false); + let ty = Type::new(Some("".into()), None, TypeKind::TypeParam, false, false, false); assert!(ty.is_invalid_type_param()) } @@ -752,6 +843,8 @@ impl Type { _ => {} } + let is_typedef = ty.is_typedef(); + // Objective C template type parameter // FIXME: This is probably wrong, we are attempting to find the // objc template params, which seem to manifest as a typedef. @@ -1202,8 +1295,9 @@ impl Type { let name = if name.is_empty() { None } else { Some(name) }; let is_const = ty.is_const(); + let is_volatile = ty.is_volatile(); - let ty = Type::new(name, layout, kind, is_const); + let ty = Type::new(name, layout, kind, is_const, is_volatile, is_typedef); // TODO: maybe declaration.canonical()? Ok(ParseResult::New(ty, Some(cursor.canonical()))) } diff --git a/src/ir/var.rs b/src/ir/var.rs index 14f133fd45..e15e2832a9 100644 --- a/src/ir/var.rs +++ b/src/ir/var.rs @@ -28,6 +28,15 @@ pub enum VarType { String(Vec), } +/// The thread-local storage (TLS) kind. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Tls { + /// dynamic TLS mode. + Dynamic, + /// static TLS mode. + Static, +} + /// A `Var` is our intermediate representation of a variable. #[derive(Debug)] pub struct Var { @@ -41,6 +50,8 @@ pub struct Var { val: Option, /// Whether this variable is const. is_const: bool, + /// Whether this variable is in thread-local storage (TLS) mode. + tls: Option, } impl Var { @@ -51,6 +62,7 @@ impl Var { ty: TypeId, val: Option, is_const: bool, + tls: Option, ) -> Var { assert!(!name.is_empty()); Var { @@ -59,6 +71,7 @@ impl Var { ty, val, is_const, + tls, } } @@ -72,6 +85,11 @@ impl Var { self.val.as_ref() } + /// Whether this variable is in thread-local storage (TLS) mode. + pub fn tls(&self) -> Option { + self.tls + } + /// Get this variable's type. pub fn ty(&self) -> TypeId { self.ty @@ -197,6 +215,8 @@ impl ClangSubItemParser for Var { let char_ty = Item::builtin_type( TypeKind::Int(IntKind::U8), true, + false, + false, ctx, ); if let Some(callbacks) = ctx.parse_callbacks() { @@ -213,10 +233,10 @@ impl ClangSubItemParser for Var { } }; - let ty = Item::builtin_type(type_kind, true, ctx); + let ty = Item::builtin_type(type_kind, true, false, false, ctx); Ok(ParseResult::New( - Var::new(name, None, ty, Some(val), true), + Var::new(name, None, ty, Some(val), true, None), Some(cursor), )) } @@ -228,6 +248,15 @@ impl ClangSubItemParser for Var { } let ty = cursor.cur_type(); + let tls = match cursor.tls() { + CXTLS_Dynamic => Some(Tls::Dynamic), + CXTLS_Static => Some(Tls::Static), + _ => None, + }; + + if !ctx.options().generate_tls_vars && tls.is_some() { + return Err(ParseError::Continue); + } // XXX this is redundant, remove! let is_const = ty.is_const(); @@ -290,7 +319,7 @@ impl ClangSubItemParser for Var { }; let mangling = cursor_mangling(ctx, &cursor); - let var = Var::new(name, mangling, ty, value, is_const); + let var = Var::new(name, mangling, ty, value, is_const, tls); Ok(ParseResult::New(var, Some(cursor))) } diff --git a/src/lib.rs b/src/lib.rs index a0202d18b7..ae4aff03ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,12 @@ fn args_are_cpp(clang_args: &[String]) -> bool { .any(|w| w[0] == "-xc++" || w[1] == "-xc++" || w == &["-x", "c++"]); } +fn header_is_cpp(name_file: &str) -> bool { + name_file.ends_with(".hpp") || name_file.ends_with(".hxx") + || name_file.ends_with(".hh") + || name_file.ends_with(".h++") +} + bitflags! { /// A type used to indicate which kind of items we have to generate. pub struct CodegenConfig: u32 { @@ -1118,6 +1124,12 @@ impl Builder { self } + /// Whether TLS variables should be generated or not. + pub fn generate_tls_vars(mut self, doit: bool) -> Self { + self.options.generate_tls_vars = doit; + self + } + /// Ignore functions. pub fn ignore_functions(mut self) -> Builder { self.options.codegen_config.remove(CodegenConfig::FUNCTIONS); @@ -1222,12 +1234,6 @@ impl Builder { /// issues. The resulting file will be named something like `__bindgen.i` or /// `__bindgen.ii` pub fn dump_preprocessed_input(&self) -> io::Result<()> { - fn check_is_cpp(name_file: &str) -> bool { - name_file.ends_with(".hpp") || name_file.ends_with(".hxx") - || name_file.ends_with(".hh") - || name_file.ends_with(".h++") - } - let clang = clang_sys::support::Clang::find(None, &[]).ok_or_else(|| { io::Error::new(io::ErrorKind::Other, "Cannot find clang executable") })?; @@ -1241,7 +1247,7 @@ impl Builder { // For each input header, add `#include "$header"`. for header in &self.input_headers { - is_cpp |= check_is_cpp(header); + is_cpp |= header_is_cpp(header); wrapper_contents.push_str("#include \""); wrapper_contents.push_str(header); @@ -1251,7 +1257,7 @@ impl Builder { // For each input header content, add a prefix line of `#line 0 "$name"` // followed by the contents. for &(ref name, ref contents) in &self.input_header_contents { - is_cpp |= check_is_cpp(name); + is_cpp |= header_is_cpp(name); wrapper_contents.push_str("#line 0 \""); wrapper_contents.push_str(name); @@ -1494,6 +1500,9 @@ struct BindgenOptions { /// Whether to generate inline functions. Defaults to false. generate_inline_functions: bool, + /// Whether to generate TLS variables. Defaults to false. + generate_tls_vars: bool, + /// Whether to whitelist types recursively. Defaults to true. whitelist_recursively: bool, @@ -1580,6 +1589,17 @@ impl BindgenOptions { pub fn rust_features(&self) -> RustFeatures { self.rust_features } + + /// Get the input header file. + pub fn input_header(&self) -> Option<&str> { + self.input_header.as_ref().map(|s| s.as_str()) + } + + /// Whether the header is c++ mode. + pub fn is_cpp(&self) -> bool { + args_are_cpp(&self.clang_args) || + self.input_header.as_ref().map(|filename| header_is_cpp(filename)).unwrap_or(false) + } } impl Default for BindgenOptions { @@ -1634,6 +1654,7 @@ impl Default for BindgenOptions { conservative_inline_namespaces: false, generate_comments: true, generate_inline_functions: false, + generate_tls_vars: false, whitelist_recursively: true, generate_block: false, objc_extern_crate: false, diff --git a/src/options.rs b/src/options.rs index 3594be4e24..99cdf5e70f 100644 --- a/src/options.rs +++ b/src/options.rs @@ -251,6 +251,9 @@ where Arg::with_name("generate-inline-functions") .long("generate-inline-functions") .help("Generate inline functions."), + Arg::with_name("generate-tls-vars") + .long("generate-tls-vars") + .help("Generate tls variables."), Arg::with_name("whitelist-type") .long("whitelist-type") .help("Only generate types matching . Other non-whitelisted types will \ @@ -548,6 +551,10 @@ where builder = builder.generate_inline_functions(true); } + if matches.is_present("generate-tls-vars") { + builder = builder.generate_tls_vars(true); + } + if let Some(whitelist) = matches.values_of("whitelist-function") { for regex in whitelist { builder = builder.whitelist_function(regex); diff --git a/src/parse.rs b/src/parse.rs index 1a9278b300..ed28767d1c 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -97,6 +97,8 @@ pub trait ClangItemParser: Sized { fn builtin_type( kind: TypeKind, is_const: bool, + is_volatile: bool, + is_typedef: bool, context: &mut BindgenContext, ) -> TypeId; } diff --git a/tests/expectations/tests/generate-c-inline.rs b/tests/expectations/tests/generate-c-inline.rs new file mode 100644 index 0000000000..0a8276dd12 --- /dev/null +++ b/tests/expectations/tests/generate-c-inline.rs @@ -0,0 +1,55 @@ +/* automatically generated by rust-bindgen */ + +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[doc(hidden)] +macro_rules ! c { ( ) => { } ; ( # include $ filename : tt $ ( $ rest : tt ) * ) => { c ! { $ ( $ rest ) * } } ; ( { $ ( $ code : tt ) * } $ ( $ rest : tt ) * ) => { c ! { $ ( $ rest ) * } } ; } +c! { # include "generate-c-inline.h" } +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct state_t { + pub foo: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_state_t() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(state_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(state_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).foo as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(state_t), + "::", + stringify!(foo) + ) + ); +} +extern "C" { + #[link_name = "\u{1}__inlined_state"] + pub fn state(s: *mut state_t) -> state_t; +} +c! { { state_t __inlined_state ( state_t * s ) { return state ( s ) ; } } } +extern "C" { + #[link_name = "\u{1}__inlined_foo"] + pub fn foo(x: ::std::os::raw::c_int, y: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +c! { { int __inlined_foo ( const int x , const int y ) { return foo ( x , y ) ; } } } +extern "C" { + #[link_name = "\u{1}__inlined_nop"] + pub fn nop(); +} +c! { { void __inlined_nop ( ) { nop ( ) ; } } } diff --git a/tests/expectations/tests/generate-inline.rs b/tests/expectations/tests/generate-inline.rs index 48d8f4f898..db91847d18 100644 --- a/tests/expectations/tests/generate-inline.rs +++ b/tests/expectations/tests/generate-inline.rs @@ -1,9 +1,15 @@ /* automatically generated by rust-bindgen */ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] -#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] - - +#[doc(hidden)] +macro_rules ! cpp { ( ) => { } ; ( # include $ filename : tt $ ( $ rest : tt ) * ) => { cpp ! { $ ( $ rest ) * } } ; ( { $ ( $ code : tt ) * } $ ( $ rest : tt ) * ) => { cpp ! { $ ( $ rest ) * } } ; } +cpp! { # include "generate-inline.hpp" } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct Foo { @@ -36,3 +42,8 @@ extern "C" { #[link_name = "\u{1}_Z3foov"] pub fn foo() -> ::std::os::raw::c_int; } +extern "C" { + #[link_name = "\u{1}_ZL3barii"] + pub fn bar(x: ::std::os::raw::c_int, y: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +cpp! { { int _ZL3barii ( const int x , const int y ) { return bar ( x , y ) ; } } } diff --git a/tests/expectations/tests/thread_local_vars.rs b/tests/expectations/tests/thread_local_vars.rs new file mode 100644 index 0000000000..71af58618f --- /dev/null +++ b/tests/expectations/tests/thread_local_vars.rs @@ -0,0 +1,34 @@ +/* automatically generated by rust-bindgen */ + +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +macro_rules ! c { ( ) => { } ; ( # include $ filename : tt $ ( $ rest : tt ) * ) => { c ! { $ ( $ rest ) * } } ; ( { $ ( $ code : tt ) * } $ ( $ rest : tt ) * ) => { c ! { $ ( $ rest ) * } } ; } +c! { # include "thread_local_vars.h" } +extern "C" { + pub fn i() -> ::std::os::raw::c_int; + pub fn set_i(v: ::std::os::raw::c_int); +} +c! { { int i ( ) { return i ; } } } +c! { { void set_i ( int v ) { i = v ; } } } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct state { + _unused: [u8; 0], +} +extern "C" { + pub fn s() -> state; + pub fn set_s(v: state); +} +c! { { struct state s ( ) { return s ; } } } +c! { { void set_s ( struct state v ) { s = v ; } } } +extern "C" { + pub fn p() -> *mut ::std::os::raw::c_char; + pub fn set_p(v: *mut ::std::os::raw::c_char); +} +c! { { char * p ( ) { return p ; } } } +c! { { void set_p ( char * v ) { p = v ; } } } diff --git a/tests/headers/generate-c-inline.h b/tests/headers/generate-c-inline.h new file mode 100644 index 0000000000..a189649042 --- /dev/null +++ b/tests/headers/generate-c-inline.h @@ -0,0 +1,11 @@ +// bindgen-flags: --generate-inline-functions + +typedef struct { + int foo; +} state_t; + +static inline state_t state(state_t *s) { return *s; } + +inline static int foo(const int x, const int y) { return x + y; } +static int bar(const int x, const int y) { return x - y; } +inline static void nop() { return; } diff --git a/tests/headers/generate-inline.hpp b/tests/headers/generate-inline.hpp index 922ee1ca7c..1dee97a0d4 100644 --- a/tests/headers/generate-inline.hpp +++ b/tests/headers/generate-inline.hpp @@ -10,3 +10,4 @@ class Foo { inline int foo() { return 42; } +inline static int bar(const int x, const int y) { return x + y; } diff --git a/tests/headers/thread_local_vars.h b/tests/headers/thread_local_vars.h new file mode 100644 index 0000000000..44c84efe67 --- /dev/null +++ b/tests/headers/thread_local_vars.h @@ -0,0 +1,6 @@ +// bindgen-flags: --generate-tls-vars +// bindgen-generate-bindings-on-linux-only + +__thread int i; +extern __thread struct state s; +static __thread char *p;