#![crate_name = "bindgen"]
#![crate_type = "dylib"]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![warn(missing_docs)]
#![doc(html_root_url = "https://crabtw.github.io/rust-bindgen/")]
extern crate clang_sys;
extern crate syntex_syntax as syntax;
extern crate libc;
#[macro_use]
extern crate log;
extern crate cexpr;
use std::collections::HashSet;
use std::default::Default;
use std::io::{self, Write};
use std::fs::OpenOptions;
use std::path::Path;
use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::print::pprust;
use syntax::print::pp::eof;
use syntax::ptr::P;
use types::Global;
use clang_sys::support::Clang;
mod types;
mod clang;
mod gen;
mod parser;
#[derive(Debug, Clone)]
pub struct Builder<'a> {
options: BindgenOptions,
logger: Option<&'a Logger>,
}
impl<'a> Builder<'a> {
pub fn new<T: Into<String>>(header: T) -> Builder<'a> {
let mut builder = Builder {
logger: None,
options: Default::default(),
};
builder.clang_arg(header);
builder
}
pub fn match_pat<T: Into<String>>(&mut self, arg: T) -> &mut Self {
self.options.match_pat.push(arg.into());
self
}
pub fn clang_arg<T: Into<String>>(&mut self, arg: T) -> &mut Self {
self.options.clang_args.push(arg.into());
self
}
pub fn link<T: Into<String>>(&mut self, library: T, link_type: LinkType) -> &mut Self {
self.options.links.push((library.into(), link_type));
self
}
pub fn forbid_unknown_types(&mut self) -> &mut Self {
self.options.fail_on_unknown_type = true;
self
}
pub fn builtins(&mut self) -> &mut Self {
self.options.builtins = true;
self
}
pub fn derive_debug(&mut self, derive_debug: bool) -> &mut Self {
self.options.derive_debug = derive_debug;
self
}
pub fn rust_enums(&mut self, value: bool) -> &mut Self {
self.options.rust_enums = value;
self
}
pub fn log(&mut self, logger: &'a Logger) -> &mut Self {
self.logger = Some(logger);
self
}
pub fn override_enum_ty<T: Into<String>>(&mut self, ty: T) -> &mut Self {
self.options.override_enum_ty = ty.into();
self
}
pub fn remove_prefix<T: Into<String>>(&mut self, ty: T) -> &mut Self {
self.options.remove_prefix = ty.into();
self
}
pub fn emit_ast(&mut self, value: bool) -> &mut Self {
self.options.emit_ast = value;
self
}
pub fn use_core(&mut self, value: bool) -> &mut Self {
self.options.use_core = value;
self
}
pub fn ctypes_prefix<T: Into<Vec<String>>>(&mut self, prefix: T) -> &mut Self {
self.options.ctypes_prefix = prefix.into();
self
}
pub fn dont_convert_floats(&mut self) -> &mut Self {
self.options.convert_floats = false;
self
}
pub fn convert_macros(&mut self, value: bool) -> &mut Self {
self.options.convert_macros = value;
self
}
pub fn macro_int_types<T: Into<String>, I: Iterator<Item=T>>(&mut self, mut types: I) -> &mut Self {
self.options.macro_int_types = (
types.next().expect("Not enough types in macro_int_types").into(),
types.next().expect("Not enough types in macro_int_types").into(),
types.next().expect("Not enough types in macro_int_types").into(),
types.next().expect("Not enough types in macro_int_types").into(),
types.next().expect("Not enough types in macro_int_types").into(),
types.next().expect("Not enough types in macro_int_types").into(),
types.next().expect("Not enough types in macro_int_types").into(),
types.next().expect("Not enough types in macro_int_types").into(),
);
if let Some(_)=types.next() {
panic!("Additional type specified in macro_int_types")
}
self
}
pub fn generate(&self) -> Result<Bindings, ()> {
Bindings::generate(&self.options, self.logger, None)
}
}
#[derive(Debug, Clone)]
#[doc(hidden)]
pub struct BindgenOptions {
pub match_pat: Vec<String>,
pub builtins: bool,
pub rust_enums: bool,
pub links: Vec<(String, LinkType)>,
pub emit_ast: bool,
pub fail_on_unknown_type: bool,
pub override_enum_ty: String,
pub clang_args: Vec<String>,
pub derive_debug: bool,
pub ctypes_prefix: Vec<String>,
pub use_core: bool,
pub remove_prefix: String,
pub convert_floats: bool,
pub convert_macros: bool,
pub macro_int_types: (String,String,String,String,String,String,String,String),
}
impl Default for BindgenOptions {
fn default() -> BindgenOptions {
let clang = Clang::find(None).expect("No clang found, is it installed?");
let mut args = Vec::new();
for dir in clang.c_search_paths {
args.push("-idirafter".to_owned());
args.push(dir.to_str().unwrap().to_owned());
}
if cfg!(target_os="macos") {
args.push("-U__BLOCKS__".to_owned());
}
BindgenOptions {
match_pat: Vec::new(),
builtins: false,
rust_enums: true,
links: Vec::new(),
emit_ast: false,
fail_on_unknown_type: true,
override_enum_ty: "".to_owned(),
clang_args: args,
derive_debug: true,
ctypes_prefix: vec!["std".into(), "os".into(), "raw".into()],
use_core: false,
remove_prefix: String::new(),
convert_floats: true,
convert_macros: false,
macro_int_types: ("uchar".to_owned(),"ushort".to_owned(),"uint".to_owned(),"ulonglong".to_owned(),"schar".to_owned(),"sshort".to_owned(),"sint".to_owned(),"slonglong".to_owned())
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum LinkType {
Static,
Dynamic,
Framework,
}
pub trait Logger: std::fmt::Debug {
fn error(&self, msg: &str) {
error!("{}", msg);
}
fn warn(&self, msg: &str) {
warn!("{}", msg);
}
}
#[derive(Clone)]
pub struct Bindings {
module: ast::Mod,
attributes: Vec<ast::Attribute>,
}
impl Bindings {
#[doc(hidden)]
pub fn generate(options: &BindgenOptions,
logger: Option<&Logger>,
span: Option<Span>)
-> Result<Bindings, ()> {
let l = DummyLogger;
let logger = match logger {
Some(l) => l,
None => &l as &Logger,
};
let span = match span {
Some(s) => s,
None => DUMMY_SP,
};
let globals = try!(parse_headers(options, logger));
let (m, attrs) = gen::gen_mod(options, globals, span);
let module = ast::Mod {
inner: span,
items: m,
};
Ok(Bindings {
module: module,
attributes: attrs,
})
}
pub fn into_ast(self) -> Vec<P<ast::Item>> {
self.module.items
}
pub fn to_string(&self) -> String {
let mut mod_str = Vec::new();
{
let ref_writer = Box::new(mod_str.by_ref()) as Box<Write>;
self.write(ref_writer).expect("Could not write bindings to string");
}
String::from_utf8(mod_str).unwrap()
}
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let file = try!(OpenOptions::new().write(true).truncate(true).create(true).open(path));
self.write(Box::new(file))
}
#[cfg_attr(feature = "clippy", allow(needless_lifetimes))]
pub fn write<'a>(&self, mut writer: Box<Write + 'a>) -> io::Result<()> {
try!(writer.write("/* automatically generated by rust-bindgen */\n\n".as_bytes()));
let mut ps = pprust::rust_printer(writer);
try!(ps.print_mod(&self.module, &self.attributes));
try!(ps.print_remaining_comments());
try!(eof(&mut ps.s));
ps.s.out.flush()
}
}
#[derive(Debug)]
struct DummyLogger;
impl Logger for DummyLogger {}
fn parse_headers(options: &BindgenOptions, logger: &Logger) -> Result<Vec<Global>, ()> {
fn str_to_ikind(s: &str) -> Option<types::IKind> {
match s {
"uchar" => Some(types::IUChar),
"schar" => Some(types::ISChar),
"ushort" => Some(types::IUShort),
"sshort" => Some(types::IShort),
"uint" => Some(types::IUInt),
"sint" => Some(types::IInt),
"ulong" => Some(types::IULong),
"slong" => Some(types::ILong),
"ulonglong" => Some(types::IULongLong),
"slonglong" => Some(types::ILongLong),
_ => None,
}
}
let m_ty=parser::MacroTypes{
t_u8: str_to_ikind(&options.macro_int_types.0).expect("Invalid C type specified for u8"),
t_u16: str_to_ikind(&options.macro_int_types.1).expect("Invalid C type specified for u16"),
t_u32: str_to_ikind(&options.macro_int_types.2).expect("Invalid C type specified for u32"),
t_u64: str_to_ikind(&options.macro_int_types.3).expect("Invalid C type specified for u64"),
t_i8: str_to_ikind(&options.macro_int_types.4).expect("Invalid C type specified for i8"),
t_i16: str_to_ikind(&options.macro_int_types.5).expect("Invalid C type specified for i16"),
t_i32: str_to_ikind(&options.macro_int_types.6).expect("Invalid C type specified for i32"),
t_i64: str_to_ikind(&options.macro_int_types.7).expect("Invalid C type specified for i64"),
};
let clang_opts = parser::ClangParserOptions {
builtin_names: builtin_names(),
builtins: options.builtins,
match_pat: options.match_pat.clone(),
emit_ast: options.emit_ast,
fail_on_unknown_type: options.fail_on_unknown_type,
override_enum_ty: str_to_ikind(&options.override_enum_ty[..]),
clang_args: options.clang_args.clone(),
macros: options.convert_macros,
macro_types: m_ty,
};
parser::parse(clang_opts, logger)
}
fn builtin_names() -> HashSet<String> {
let mut names = HashSet::new();
let keys = ["__va_list_tag", "__va_list", "__builtin_va_list"];
for s in &keys {
names.insert((*s).to_owned());
}
names
}
#[test]
fn builder_state() {
let logger = DummyLogger;
let mut build = Builder::new("example.h");
build.link("m", LinkType::Static)
.log(&logger);
assert!(build.logger.is_some());
assert!(build.options.clang_args.binary_search(&"example.h".to_owned()).is_ok());
assert!(build.options.links.binary_search(&("m".to_owned(), LinkType::Static)).is_ok());
}