diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index a03b2826d..400a04ee0 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -29,6 +29,7 @@ pub struct Bindings { constants: Vec, items: Vec, functions: Vec, + source_files: Vec, /// Bindings are generated by a recursive call to cbindgen /// and shouldn't do anything when written anywhere. noop: bool, @@ -50,6 +51,7 @@ impl Bindings { globals: Vec, items: Vec, functions: Vec, + source_files: Vec, noop: bool, ) -> Bindings { Bindings { @@ -61,6 +63,7 @@ impl Bindings { constants, items, functions, + source_files, noop, } } @@ -128,6 +131,53 @@ impl Bindings { fields } + pub fn generate_depfile>( + &self, + header_path: P, + depfile_path: P, + config_file: Option<&str>, + ) { + if let Some(dir) = depfile_path.as_ref().parent() { + if !dir.exists() { + std::fs::create_dir_all(dir).unwrap() + } + } + let canon_header_path = header_path.as_ref().canonicalize().unwrap(); + let mut canon_source_files: Vec<_> = self + .source_files + .iter() + .map(|p| p.canonicalize().unwrap()) + .collect(); + // Changing the config-file should also re-trigger cbindgen. + if let Some(file) = config_file { + canon_source_files.push(PathBuf::from(file).canonicalize().unwrap()) + } + + // When writing the depfile we must escape whitespace in paths to avoid it being interpreted + // as a seperator. + // It is not clear how to otherwise _correctly_ replace whitespace in a non-unicode + // compliant slice, without knowing the encoding, so we lossy convert such cases, + // to avoid panics. + let mut depfile = File::create(depfile_path).unwrap(); + write!( + &mut depfile, + "{}:", + canon_header_path.to_string_lossy().replace(' ', "\\ ") + ) + .expect("Writing header name to depfile failed"); + canon_source_files.into_iter().for_each(|source_file| { + // Add line-continue and line-break and then indent with 4 spaces. + // This makes the output more human-readable. + depfile.write_all(b" \\\n ").unwrap(); + let escaped_path = source_file.to_string_lossy().replace(' ', "\\ "); + depfile.write_all(escaped_path.as_bytes()).unwrap(); + }); + + writeln!(&mut depfile).unwrap(); + + depfile.flush().unwrap(); + } + pub fn write_to_file>(&self, path: P) -> bool { if self.noop { return false; diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index 17d4ad1df..a0328b409 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -359,6 +359,7 @@ impl Builder { Default::default(), Default::default(), Default::default(), + Default::default(), true, )); } @@ -391,6 +392,8 @@ impl Builder { result.extend_with(&parser::parse_lib(cargo, &self.config)?); } + result.source_files.extend_from_slice(self.srcs.as_slice()); + Library::new( self.config, result.constants, @@ -401,6 +404,7 @@ impl Builder { result.opaque_items, result.typedefs, result.functions, + result.source_files, ) .generate() } diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index c00a6ebc6..cb4cfbd3a 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; +use std::path::PathBuf; use crate::bindgen::bindings::Bindings; use crate::bindgen::config::{Config, Language, SortKey}; @@ -25,6 +26,7 @@ pub struct Library { opaque_items: ItemMap, typedefs: ItemMap, functions: Vec, + source_files: Vec, } impl Library { @@ -39,6 +41,7 @@ impl Library { opaque_items: ItemMap, typedefs: ItemMap, functions: Vec, + source_files: Vec, ) -> Library { Library { config, @@ -50,6 +53,7 @@ impl Library { opaque_items, typedefs, functions, + source_files, } } @@ -135,6 +139,7 @@ impl Library { globals, items, functions, + self.source_files, false, )) } diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index a964cd2d8..9bdfef83b 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -181,6 +181,8 @@ impl<'a> Parser<'a> { } } + self.out.source_files = self.cache_src.keys().map(|k| k.to_owned()).collect(); + Ok(()) } @@ -406,6 +408,7 @@ pub struct Parse { pub opaque_items: ItemMap, pub typedefs: ItemMap, pub functions: Vec, + pub source_files: Vec, } impl Parse { @@ -419,6 +422,7 @@ impl Parse { opaque_items: ItemMap::default(), typedefs: ItemMap::default(), functions: Vec::new(), + source_files: Vec::new(), } } @@ -466,6 +470,7 @@ impl Parse { self.opaque_items.extend_with(&other.opaque_items); self.typedefs.extend_with(&other.typedefs); self.functions.extend_from_slice(&other.functions); + self.source_files.extend_from_slice(&other.source_files); } fn load_syn_crate_mod<'a>( diff --git a/src/main.rs b/src/main.rs index 812366f49..13dff0553 100644 --- a/src/main.rs +++ b/src/main.rs @@ -262,6 +262,20 @@ fn main() { .help("Report errors only (overrides verbosity options).") .required(false), ) + .arg( + Arg::new("depfile") + .value_name("PATH") + .long("depfile") + .takes_value(true) + .min_values(1) + .max_values(1) + .required(false) + .help("Generate a depfile at the given Path listing the source files \ + cbindgen traversed when generating the bindings. Useful when \ + integrating cbindgen into 3rd party build-systems. \ + This option is ignored if `--out` is missing." + ) + ) .get_matches(); if !matches.is_present("out") && matches.is_present("verify") { @@ -306,6 +320,9 @@ fn main() { error!("Bindings changed: {}", file); std::process::exit(2); } + if let Some(depfile) = matches.value_of("depfile") { + bindings.generate_depfile(file, depfile, matches.value_of("config")) + } } _ => { bindings.write(io::stdout());