Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update syntex and use clang-sys for path-searching #11

Merged
merged 2 commits into from
Jul 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ build = "build.rs"
clippy = { version = "*", optional = true }
log = "0.3.*"
libc = "0.2.*"
syntex_syntax = "0.32"
syntex_syntax = "0.37"
clang-sys = "0.7.2"

[features]
static = []
Expand Down
13 changes: 8 additions & 5 deletions src/bin/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
extern crate bindgen;
#[macro_use]
extern crate log;
extern crate clang_sys;

use bindgen::{Bindings, BindgenOptions, LinkType, Logger};
use std::io;
Expand Down Expand Up @@ -214,12 +215,14 @@ pub fn main() {
let mut bind_args: Vec<_> = env::args().collect();
let bin = bind_args.remove(0);

match bindgen::get_include_dir() {
Some(path) => {
bind_args.push("-I".to_owned());
bind_args.push(path);
if let Some(clang) = clang_sys::support::Clang::find(None) {
// TODO: distinguish C and C++ paths? C++'s should be enough, I guess.
for path in clang.cpp_search_paths.into_iter() {
if let Ok(path) = path.into_os_string().into_string() {
bind_args.push("-isystem".to_owned());
bind_args.push(path);
}
}
None => (),
}

match parse_args(&bind_args) {
Expand Down
138 changes: 82 additions & 56 deletions src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,21 +150,17 @@ fn enum_name(ctx: &GenCtx, name: &str) -> String {
fn gen_unmangle_method(ctx: &mut GenCtx,
v: &VarInfo,
counts: &mut HashMap<String, isize>,
self_kind: ast::SelfKind)
self_kind: Option<ast::Mutability>)
-> ast::ImplItem {
let fndecl;
let mut fndecl;
let mut args = vec![];

match self_kind {
ast::SelfKind::Static => (),
ast::SelfKind::Region(_, mutable, _) => {
let selfexpr = match mutable {
ast::Mutability::Immutable => quote_expr!(&ctx.ext_cx, &*self),
ast::Mutability::Mutable => quote_expr!(&ctx.ext_cx, &mut *self),
};
args.push(selfexpr);
},
_ => unreachable!()
if let Some(mutability) = self_kind {
let selfexpr = match mutability {
ast::Mutability::Immutable => quote_expr!(&ctx.ext_cx, &*self),
ast::Mutability::Mutable => quote_expr!(&ctx.ext_cx, &mut *self),
};
args.push(selfexpr);
}

match v.ty {
Expand All @@ -173,13 +169,12 @@ fn gen_unmangle_method(ctx: &mut GenCtx,
&*sig.ret_ty, sig.args.as_slice(),
false);
let mut unnamed: usize = 0;
let iter = if args.len() > 0 {
let iter = if !args.is_empty() {
sig.args[1..].iter()
} else {
sig.args.iter()
};
for arg in iter {
let (ref n, _) = *arg;
for &(ref n, _) in iter {
let argname = if n.is_empty() {
unnamed += 1;
format!("arg{}", unnamed)
Expand All @@ -197,46 +192,79 @@ fn gen_unmangle_method(ctx: &mut GenCtx,
})
}),
span: ctx.span,
attrs: None,
attrs: ast::ThinVec::new(),
};
args.push(P(expr));
}
},
_ => unreachable!()
};


if let Some(mutability) = self_kind {
assert!(!fndecl.inputs.is_empty());
fndecl.inputs[0] = ast::Arg {
ty: P(ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ast::TyKind::Rptr(None, ast::MutTy {
ty: P(ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ast::TyKind::ImplicitSelf,
span: ctx.span
}),
mutbl: mutability,
}),
span: ctx.span,
}),
pat: P(ast::Pat {
id: ast::DUMMY_NODE_ID,
node: ast::PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable),
respan(ctx.span, ctx.ext_cx.ident_of("self")),
None),
span: ctx.span,
}),
id: ast::DUMMY_NODE_ID,
};
}

let sig = ast::MethodSig {
unsafety: ast::Unsafety::Unsafe,
abi: Abi::Rust,
decl: P(fndecl),
generics: empty_generics(),
explicit_self: respan(ctx.span, self_kind),
constness: ast::Constness::NotConst,
};

let block = ast::Block {
stmts: vec![],
expr: Some(P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Call(
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Path(None, ast::Path {
span: ctx.span,
global: false,
segments: vec!(ast::PathSegment {
identifier: ctx.ext_cx.ident_of(&v.mangled),
parameters: ast::PathParameters::none()
})
}),
let call = P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Call(
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Path(None, ast::Path {
span: ctx.span,
attrs: None,
global: false,
segments: vec![ast::PathSegment {
identifier: ctx.ext_cx.ident_of(&v.mangled),
parameters: ast::PathParameters::none()
}]
}),
args
),
span: ctx.span,
attrs: None,
})),
span: ctx.span,
attrs: ast::ThinVec::new(),
}),
args
),
span: ctx.span,
attrs: ast::ThinVec::new(),
});

let block = ast::Block {
stmts: vec![
ast::Stmt {
id: ast::DUMMY_NODE_ID,
node: ast::StmtKind::Expr(call),
span: ctx.span,
}
],
id: ast::DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default,
span: ctx.span
Expand Down Expand Up @@ -282,16 +310,12 @@ pub fn gen_mods(links: &[(String, LinkType)],
// Create a dummy ExtCtxt. We only need this for string interning and that uses TLS.
let mut features = Features::new();
features.quote = true;
let cfg = ExpansionConfig {
crate_name: "xxx".to_owned(),
features: Some(&features),
recursion_limit: 64,
trace_mac: false,
};
let sess = &parse::ParseSess::new();
let mut feature_gated_cfgs = vec![];

let cfg = ExpansionConfig::default("xxx".to_owned());
let sess = parse::ParseSess::new();
let mut loader = base::DummyMacroLoader;
let mut ctx = GenCtx {
ext_cx: base::ExtCtxt::new(sess, vec![], cfg, &mut feature_gated_cfgs),
ext_cx: base::ExtCtxt::new(&sess, vec![], cfg, &mut loader),
options: options,
span: span,
module_map: map,
Expand Down Expand Up @@ -1220,11 +1244,11 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item>
TFuncPtr(ref sig) => {
let name = v.mangled.clone();
let explicit_self = if v.is_static {
ast::SelfKind::Static
None
} else if v.is_const {
ast::SelfKind::Region(None, ast::Mutability::Immutable, ctx.ext_cx.ident_of("self"))
Some(ast::Mutability::Immutable)
} else {
ast::SelfKind::Region(None, ast::Mutability::Mutable, ctx.ext_cx.ident_of("self"))
Some(ast::Mutability::Mutable)
};
unmangledlist.push(gen_unmangle_method(ctx, &v, &mut unmangle_count, explicit_self));
mangledlist.push(cfunc_to_rs(ctx, name, String::new(), String::new(),
Expand Down Expand Up @@ -1698,8 +1722,6 @@ fn gen_fullbitfield_method(ctx: &mut GenCtx, bindgen_name: &String,
variadic: false
};

let stmts = Vec::with_capacity(bitfields.len() + 1);

let mut offset = 0;

let mut exprs = quote_expr!(&ctx.ext_cx, 0);
Expand All @@ -1721,8 +1743,13 @@ fn gen_fullbitfield_method(ctx: &mut GenCtx, bindgen_name: &String,
}

let block = ast::Block {
stmts: stmts,
expr: Some(exprs),
stmts: vec![
ast::Stmt {
id: ast::DUMMY_NODE_ID,
node: ast::StmtKind::Expr(exprs),
span: ctx.span,
}
],
id: ast::DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default,
span: ctx.span
Expand All @@ -1734,7 +1761,6 @@ fn gen_fullbitfield_method(ctx: &mut GenCtx, bindgen_name: &String,
abi: Abi::Rust,
decl: P(fndecl),
generics: empty_generics(),
explicit_self: respan(ctx.span, ast::SelfKind::Static),
constness: ast::Constness::Const,
}, P(block)
);
Expand Down Expand Up @@ -2151,7 +2177,7 @@ fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: usize) -> ast::Ty {
id: ast::DUMMY_NODE_ID,
node: sz,
span: ctx.span,
attrs: None,
attrs: ast::ThinVec::new(),
})
);

Expand Down
74 changes: 3 additions & 71 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#![cfg_attr(feature = "clippy", plugin(clippy))]

extern crate syntex_syntax as syntax;
extern crate clang_sys;
extern crate libc;
#[macro_use]
extern crate log;
Expand All @@ -15,8 +16,7 @@ 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 std::path::Path;

use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span};
Expand Down Expand Up @@ -332,8 +332,7 @@ fn builtin_names() -> HashSet<String> {
}

#[test]
fn builder_state()
{
fn builder_state() {
let logger = DummyLogger;
let mut build = builder();
{
Expand All @@ -345,70 +344,3 @@ fn builder_state()
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,
}
}