summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Fitzgerald <fitzgen@gmail.com>2017-07-13 14:17:53 -0700
committerNick Fitzgerald <fitzgen@gmail.com>2017-07-13 14:17:53 -0700
commit84e9521c8a5cd63cfe65f42478ca999812cd069d (patch)
treeb70f6865ec007ccc331219775aef8ff825407253
parent1b815d61df3e8736ee35f1799a5b231b0b632202 (diff)
Add the ability to dump preprocessed input headers
This is useful when debugging bindgen, using C-Reduce on an input to bindgen, or for constructing portable test cases when filing issues against bindgen. Fixes #811
-rw-r--r--src/lib.rs74
-rw-r--r--src/options.rs10
-rw-r--r--tests/tests.rs28
3 files changed, 110 insertions, 2 deletions
diff --git a/src/lib.rs b/src/lib.rs
index a978d1b5..312155a5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -87,10 +87,11 @@ use ir::item::Item;
use parse::{ClangItemParser, ParseError};
use regex_set::RegexSet;
-use std::fs::OpenOptions;
+use std::fs::{File, OpenOptions};
use std::iter;
use std::io::{self, Write};
-use std::path::Path;
+use std::path::{Path, PathBuf};
+use std::process::Command;
use std::sync::Arc;
use syntax::ast;
@@ -814,6 +815,75 @@ impl Builder {
Bindings::generate(self.options, None)
}
+
+ /// Preprocess and dump the input header files to disk.
+ ///
+ /// This is useful when debugging bindgen, using C-Reduce, or when filing
+ /// issues. The resulting file will be named something like `__bindgen.i` or
+ /// `__bindgen.ii`
+ pub fn dump_preprocessed_input(&self) -> io::Result<()> {
+ let clang = clang_sys::support::Clang::find(None, &[])
+ .ok_or_else(|| io::Error::new(io::ErrorKind::Other,
+ "Cannot find clang executable"))?;
+
+ // The contents of a wrapper file that includes all the input header
+ // files.
+ let mut wrapper_contents = String::new();
+
+ // Whether we are working with C or C++ inputs.
+ let mut is_cpp = false;
+
+ // For each input header, add `#include "$header"`.
+ for header in &self.input_headers {
+ is_cpp |= header.ends_with(".hpp");
+
+ wrapper_contents.push_str("#include \"");
+ wrapper_contents.push_str(header);
+ wrapper_contents.push_str("\"\n");
+ }
+
+ // For each input header content, add a prefix line of `#line 0 "$name"`
+ // followed by the contents.
+ for &(ref name, ref contents) in &self.input_header_contents {
+ is_cpp |= name.ends_with(".hpp");
+
+ wrapper_contents.push_str("#line 0 \"");
+ wrapper_contents.push_str(name);
+ wrapper_contents.push_str("\"\n");
+ wrapper_contents.push_str(contents);
+ }
+
+ is_cpp |= self.options.clang_args.windows(2).any(|w| {
+ w[0] == "-x=c++" || w[1] == "-x=c++" || w == &["-x", "c++"]
+ });
+
+ let wrapper_path = PathBuf::from(if is_cpp {
+ "__bindgen.cpp"
+ } else {
+ "__bindgen.c"
+ });
+
+ {
+ let mut wrapper_file = File::create(&wrapper_path)?;
+ wrapper_file.write(wrapper_contents.as_bytes())?;
+ }
+
+ let mut cmd = Command::new(&clang.path);
+ cmd.arg("-save-temps")
+ .arg("-c")
+ .arg(&wrapper_path);
+
+ for a in &self.options.clang_args {
+ cmd.arg(a);
+ }
+
+ if cmd.spawn()?.wait()?.success() {
+ Ok(())
+ } else {
+ Err(io::Error::new(io::ErrorKind::Other,
+ "clang exited with non-zero status"))
+ }
+ }
}
/// Configuration options for generated bindings.
diff --git a/src/options.rs b/src/options.rs
index 6a6fde81..bf479ca5 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -214,6 +214,12 @@ pub fn builder_from_flags<I>
Arg::with_name("verbose")
.long("verbose")
.help("Print verbose error messages."),
+ Arg::with_name("dump-preprocessed-input")
+ .long("dump-preprocessed-input")
+ .help("Preprocess and dump the input header files to disk. \
+ Useful when debugging bindgen, using C-Reduce, or when \
+ filing issues. The resulting file will be named \
+ something like `__bindgen.i` or `__bindgen.ii`.")
]) // .args()
.get_matches_from(args);
@@ -424,6 +430,10 @@ pub fn builder_from_flags<I>
Box::new(io::BufWriter::new(io::stdout())) as Box<io::Write>
};
+ if matches.is_present("dump-preprocessed-input") {
+ builder.dump_preprocessed_input()?;
+ }
+
let verbose = matches.is_present("verbose");
Ok((builder, output, verbose))
diff --git a/tests/tests.rs b/tests/tests.rs
index 24d5770e..69500268 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -238,3 +238,31 @@ fn no_system_header_includes() {
.expect("should wait for ./ci/no-includes OK")
.success());
}
+
+#[test]
+fn dump_preprocessed_input() {
+ let arg_keyword = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/arg_keyword.hpp");
+ let empty_layout = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/cpp-empty-layout.hpp");
+
+ builder()
+ .header(arg_keyword)
+ .header(empty_layout)
+ .dump_preprocessed_input()
+ .expect("should dump preprocessed input");
+
+ fn slurp(p: &str) -> String {
+ let mut contents = String::new();
+ let mut file = fs::File::open(p).unwrap();
+ file.read_to_string(&mut contents).unwrap();
+ contents
+ }
+
+ let bindgen_ii = slurp("__bindgen.ii");
+ let arg_keyword = slurp(arg_keyword);
+ let empty_layout = slurp(empty_layout);
+
+ assert!(bindgen_ii.find(&arg_keyword).is_some(),
+ "arg_keyword.hpp is in the preprocessed file");
+ assert!(bindgen_ii.find(&empty_layout).is_some(),
+ "cpp-empty-layout.hpp is in the preprocessed file");
+}