diff --git a/src/bindgen/cargo_expand.rs b/src/bindgen/cargo_expand.rs new file mode 100644 index 0000000000000000000000000000000000000000..93ac20b9184b2f9f38774881ba6986fadef4105b --- /dev/null +++ b/src/bindgen/cargo_expand.rs @@ -0,0 +1,21 @@ +use std::env; +use std::path::Path; +use std::process::Command; +use std::str::from_utf8; + +pub fn expand(manifest_path: &Path, crate_name: &str) -> String { + let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); + let mut cmd = Command::new(cargo); + cmd.arg("rustc"); + cmd.arg("--manifest-path"); + cmd.arg(manifest_path); + cmd.arg("--all-features"); + cmd.arg("-p"); + cmd.arg(crate_name); + cmd.arg("--"); + cmd.arg("-Z"); + cmd.arg("unstable-options"); + cmd.arg("--pretty=expanded"); + let output = cmd.output().unwrap(); + from_utf8(&output.stdout).unwrap().to_owned() +} diff --git a/src/bindgen/cargo_metadata.rs b/src/bindgen/cargo_metadata.rs index 8b30eec2b75fb5df265d339fd6fd9bae22be4346..40bd39893db039144af23c6b341bd836c8f62cf8 100644 --- a/src/bindgen/cargo_metadata.rs +++ b/src/bindgen/cargo_metadata.rs @@ -110,10 +110,12 @@ impl From<serde_json::Error> for Error { pub fn metadata(manifest_path_arg: Option<&str>) -> Result<Metadata, Error> { let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); let mut cmd = Command::new(cargo); - cmd.arg("metadata").arg("--all-features"); + cmd.arg("metadata"); + cmd.arg("--all-features"); cmd.arg("--format-version").arg("1"); if let Some(mani) = manifest_path_arg { - cmd.arg("--manifest-path").arg(mani); + cmd.arg("--manifest-path"); + cmd.arg(mani); } let output = cmd.output()?; let stdout = from_utf8(&output.stdout)?; diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs index 3dabb7a43bd13a971d419eae91ee79d4a44eb15b..b24c514ecde48ea0c03ccf4f154adfaf9533136e 100644 --- a/src/bindgen/mod.rs +++ b/src/bindgen/mod.rs @@ -27,6 +27,7 @@ macro_rules! deserialize_enum_str { } } +mod cargo_expand; mod cargo_metadata; mod cdecl; mod config; diff --git a/src/bindgen/rust_lib.rs b/src/bindgen/rust_lib.rs index 4337af4c0ff4fa20eb1d03d6dcaf497d6888ef89..02fa0e59324c64d3b00c73d710857d2c712f34a3 100644 --- a/src/bindgen/rust_lib.rs +++ b/src/bindgen/rust_lib.rs @@ -3,6 +3,7 @@ use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; +use bindgen::cargo_expand; use bindgen::cargo_metadata; use syn; @@ -39,53 +40,103 @@ pub fn parse_lib<F>(crate_path: &Path, } }; - let mut items_cache = HashMap::new(); - parse_crate(binding_crate_name, - &metadata, - items_callback, - &mut items_cache); + let mut context = ParseLibContext { + manifest_path: manifest_path, + metadata: metadata, + cache_src: HashMap::new(), + cache_expanded_crate: HashMap::new(), + items_callback: items_callback, + }; + + parse_crate(binding_crate_name, &mut context); } -fn find_crate_src(package_name: &str, metadata: &cargo_metadata::Metadata) -> Option<PathBuf> { - let kind_lib = String::from("lib"); - for package in &metadata.packages { - if package.name == package_name { - for target in &package.targets { - if target.kind.contains(&kind_lib) { - return Some(PathBuf::from(&target.src_path)); - } - } - break; - } - } - None +struct ParseLibContext<F> + where F: FnMut(&str, &Vec<syn::Item>) +{ + manifest_path: PathBuf, + metadata: cargo_metadata::Metadata, + cache_src: HashMap<PathBuf, Vec<syn::Item>>, + cache_expanded_crate: HashMap<String, Vec<syn::Item>>, + + items_callback: F, +} + +impl<F> ParseLibContext<F> + where F: FnMut(&str, &Vec<syn::Item>) +{ + fn find_crate_src(&self, package_name: &str) -> Option<PathBuf> { + let kind_lib = String::from("lib"); + for package in &self.metadata.packages { + if package.name == package_name { + for target in &package.targets { + if target.kind.contains(&kind_lib) { + return Some(PathBuf::from(&target.src_path)); + } + } + break; + } + } + None + } } -fn parse_crate<F>(crate_name: &str, - metadata: &cargo_metadata::Metadata, - items_callback: &mut F, - items_cache: &mut HashMap<PathBuf, Vec<syn::Item>>) +fn parse_crate<F>(crate_name: &str, context: &mut ParseLibContext<F>) where F: FnMut(&str, &Vec<syn::Item>) { - let crate_src = find_crate_src(crate_name, metadata); + let crate_src = context.find_crate_src(crate_name); match crate_src { Some(crate_src) => { - parse_mod(crate_src.as_path(), - crate_name, - metadata, - items_callback, - items_cache); + parse_mod(crate_name, + crate_src.as_path(), + context); } None => info!("can't find crate {}", crate_name), } } -fn parse_mod<F>(mod_path: &Path, - crate_name: &str, - metadata: &cargo_metadata::Metadata, - items_callback: &mut F, - items_cache: &mut HashMap<PathBuf, Vec<syn::Item>>) +fn parse_expand_crate<F>(crate_name: &str, context: &mut ParseLibContext<F>) + where F: FnMut(&str, &Vec<syn::Item>) +{ + let mod_parsed = { + let owned_crate_name = crate_name.to_owned(); + + if !context.cache_expanded_crate.contains_key(&owned_crate_name) { + let s = cargo_expand::expand(&context.manifest_path, + crate_name); + let i = syn::parse_crate(&s).unwrap(); + context.cache_expanded_crate.insert(owned_crate_name.clone(), i.items); + } + context.cache_expanded_crate.get(&owned_crate_name).unwrap().clone() + }; + + (context.items_callback)(crate_name, + &mod_parsed); + + for item in &mod_parsed { + match item.node { + syn::ItemKind::Mod(ref inline_items) => { + if let &Some(ref inline_items) = inline_items { + (context.items_callback)(crate_name, + &inline_items); + continue; + } + + warn!("external mod found in expanded source"); + } + syn::ItemKind::ExternCrate(_) => { + parse_expand_crate(&item.ident.to_string(), + context); + } + _ => {} + } + } +} + +fn parse_mod<F>(crate_name: &str, + mod_path: &Path, + context: &mut ParseLibContext<F>) where F: FnMut(&str, &Vec<syn::Item>) { let mod_dir = mod_path.parent().unwrap().to_path_buf(); @@ -93,18 +144,18 @@ fn parse_mod<F>(mod_path: &Path, let mod_parsed = { let owned_mod_path = mod_path.to_path_buf(); - if !items_cache.contains_key(&owned_mod_path) { + if !context.cache_src.contains_key(&owned_mod_path) { let mut s = String::new(); let mut f = File::open(mod_path).unwrap(); f.read_to_string(&mut s).unwrap(); let i = syn::parse_crate(&s).unwrap(); - items_cache.insert(owned_mod_path.clone(), i.items); + context.cache_src.insert(owned_mod_path.clone(), i.items); } - items_cache.get(&owned_mod_path).unwrap().clone() + context.cache_src.get(&owned_mod_path).unwrap().clone() }; - items_callback(crate_name, - &mod_parsed); + (context.items_callback)(crate_name, + &mod_parsed); for item in &mod_parsed { match item.node { @@ -112,8 +163,8 @@ fn parse_mod<F>(mod_path: &Path, let next_mod_name = item.ident.to_string(); if let &Some(ref inline_items) = inline_items { - items_callback(crate_name, - &inline_items); + (context.items_callback)(crate_name, + &inline_items); continue; } @@ -121,26 +172,20 @@ fn parse_mod<F>(mod_path: &Path, let next_mod_path2 = mod_dir.join(next_mod_name.clone()).join("mod.rs"); if next_mod_path1.exists() { - parse_mod(next_mod_path1.as_path(), - crate_name, - metadata, - items_callback, - items_cache); + parse_mod(crate_name, + next_mod_path1.as_path(), + context); } else if next_mod_path2.exists() { - parse_mod(next_mod_path2.as_path(), - crate_name, - metadata, - items_callback, - items_cache); + parse_mod(crate_name, + next_mod_path2.as_path(), + context); } else { info!("can't find mod {} in crate {}", next_mod_name, crate_name); } } syn::ItemKind::ExternCrate(_) => { - parse_crate(&item.ident.to_string(), - metadata, - items_callback, - items_cache); + parse_expand_crate(&item.ident.to_string(), + context); } _ => {} }