blob: 29cc5fc7cd806749855fdea7b984099174dcc0fa [file] [log] [blame]
#![crate_name = "bindgen"]
#![crate_type = "dylib"]
extern crate syntex_syntax as syntax;
extern crate libc;
#[macro_use] extern crate log;
use std::collections::HashSet;
use std::default::Default;
use std::io::{Write, self};
use std::fs::OpenOptions;
use std::path::{Path, self};
use std::{env, fs};
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;
mod types;
mod clangll;
mod clang;
mod gen;
mod parser;
#[derive(Clone)]
pub struct Builder<'a> {
options: BindgenOptions,
logger: Option<&'a Logger>
}
pub fn builder<'a>() -> Builder<'a> {
Default::default()
}
impl<'a> Builder<'a> {
pub fn header<T: Into<String>>(&mut self, header: T) -> &mut Self {
self.clang_arg(header)
}
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) -> &mut Self {
self.options.links.push((library.into(), LinkType::Default));
self
}
pub fn link_static<T: Into<String>>(&mut self, library: T) -> &mut Self {
self.options.links.push((library.into(), LinkType::Static));
self
}
pub fn link_framework<T: Into<String>>(&mut self, library: T) -> &mut Self {
self.options.links.push((library.into(), LinkType::Framework));
self
}
pub fn forbid_unknown_types(&mut self) -> &mut Self {
self.options.fail_on_unknown_type = true;
self
}
pub fn emit_builtins(&mut self) -> &mut Self {
self.options.builtins = true;
self
}
pub fn log(&mut self, logger: &'a Logger) -> &mut Self {
self.logger = Some(logger);
self
}
pub fn generate(&self) -> Result<Bindings, ()> {
Bindings::generate(&self.options, self.logger, None)
}
}
impl<'a> Default for Builder<'a> {
fn default() -> Builder<'a> {
Builder {
logger: None,
options: Default::default()
}
}
}
#[derive(Clone)]
/// Deprecated - use a `Builder` instead
pub struct BindgenOptions {
pub match_pat: Vec<String>,
pub builtins: 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>,
}
impl Default for BindgenOptions {
fn default() -> BindgenOptions {
BindgenOptions {
match_pat: Vec::new(),
builtins: false,
links: Vec::new(),
emit_ast: false,
fail_on_unknown_type: false,
override_enum_ty: "".to_owned(),
clang_args: match get_include_dir() {
Some(path) => vec!("-idirafter".to_owned(), path),
None => Vec::new()
}
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum LinkType {
Default,
Static,
Framework
}
pub trait Logger {
fn error(&self, msg: &str);
fn warn(&self, msg: &str);
}
#[derive(Clone)]
pub struct Bindings {
module: ast::Mod
}
impl Bindings {
/// Deprecated - use a `Builder` instead
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 module = ast::Mod {
inner: span,
items: gen::gen_mod(&options.links[..], globals, span)
};
Ok(Bindings {
module: module
})
}
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))
}
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, &[]));
try!(ps.print_remaining_comments());
try!(eof(&mut ps.s));
ps.s.out.flush()
}
}
struct DummyLogger;
impl Logger for DummyLogger {
fn error(&self, _msg: &str) { }
fn warn(&self, _msg: &str) { }
}
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 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(),
};
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();
{
build.header("example.h");
build.link_static("m");
build.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());
}
// Get the first directory in PATH that contains a file named "clang".
fn get_clang_dir() -> Option<path::PathBuf>{
if let Some(paths) = env::var_os("PATH") {
for mut path in env::split_paths(&paths) {
path.push("clang");
if let Ok(real_path) = fs::canonicalize(&path) {
if fs::metadata(&real_path).iter().any(|m| m.is_file()) &&
real_path
.file_name()
.and_then(|f| f.to_str())
.iter()
.any(|&f| f.starts_with("clang")) {
if let Some(dir) = real_path.parent() {
return Some(dir.to_path_buf())
}
}
}
}
}
None
}
// Try to find the directory that contains clang's bundled headers. Clang itself does something
// very similar: it takes the parent directory of the current executable, appends
// "../lib/clang/<VERSIONSTRING>/include". We have two problems emulating this behaviour:
// * We don't have a very good way of finding the clang executable, but can fake this by
// searching $PATH and take one directory that contains "clang".
// * We don't have access to <VERSIONSTRING>. There is clang_getClangVersion(), but it returns
// a human-readable description string which is not guaranteed to be stable and a pain to parse.
// We work around that by just taking the first directory in ../lib/clang and hope it's the
// current version.
// TODO: test if this works on Windows at all.
#[doc(hidden)]
pub fn get_include_dir() -> Option<String> {
match get_clang_dir() {
Some(mut p) => {
p.push("..");
p.push("lib");
p.push("clang");
let dir_iter = match fs::read_dir(p) {
Ok(dir_iter) => dir_iter,
_ => return None
};
for dir in dir_iter {
match dir {
Ok(dir) => {
// Let's take the first dir. In my case, there's only one directory
// there anyway.
let mut p = dir.path();
p.push("include");
match p.into_os_string().into_string() {
Ok(s) => return Some(s),
// We found the directory, but can't access it as it contains
// invalid unicode.
_ => return None,
}
}
_ => return None,
}
}
None
}
None => None,
}
}