@@ -87,10 +87,11 @@ use ir::item::Item;
87
87
use parse:: { ClangItemParser , ParseError } ;
88
88
use regex_set:: RegexSet ;
89
89
90
- use std:: fs:: OpenOptions ;
90
+ use std:: fs:: { File , OpenOptions } ;
91
91
use std:: iter;
92
92
use std:: io:: { self , Write } ;
93
- use std:: path:: Path ;
93
+ use std:: path:: { Path , PathBuf } ;
94
+ use std:: process:: Command ;
94
95
use std:: sync:: Arc ;
95
96
96
97
use syntax:: ast;
@@ -814,6 +815,75 @@ impl Builder {
814
815
815
816
Bindings :: generate ( self . options , None )
816
817
}
818
+
819
+ /// Preprocess and dump the input header files to disk.
820
+ ///
821
+ /// This is useful when debugging bindgen, using C-Reduce, or when filing
822
+ /// issues. The resulting file will be named something like `__bindgen.i` or
823
+ /// `__bindgen.ii`
824
+ pub fn dump_preprocessed_input ( & self ) -> io:: Result < ( ) > {
825
+ let clang = clang_sys:: support:: Clang :: find ( None , & [ ] )
826
+ . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: Other ,
827
+ "Cannot find clang executable" ) ) ?;
828
+
829
+ // The contents of a wrapper file that includes all the input header
830
+ // files.
831
+ let mut wrapper_contents = String :: new ( ) ;
832
+
833
+ // Whether we are working with C or C++ inputs.
834
+ let mut is_cpp = false ;
835
+
836
+ // For each input header, add `#include "$header"`.
837
+ for header in & self . input_headers {
838
+ is_cpp |= header. ends_with ( ".hpp" ) ;
839
+
840
+ wrapper_contents. push_str ( "#include \" " ) ;
841
+ wrapper_contents. push_str ( header) ;
842
+ wrapper_contents. push_str ( "\" \n " ) ;
843
+ }
844
+
845
+ // For each input header content, add a prefix line of `#line 0 "$name"`
846
+ // followed by the contents.
847
+ for & ( ref name, ref contents) in & self . input_header_contents {
848
+ is_cpp |= name. ends_with ( ".hpp" ) ;
849
+
850
+ wrapper_contents. push_str ( "#line 0 \" " ) ;
851
+ wrapper_contents. push_str ( name) ;
852
+ wrapper_contents. push_str ( "\" \n " ) ;
853
+ wrapper_contents. push_str ( contents) ;
854
+ }
855
+
856
+ is_cpp |= self . options . clang_args . windows ( 2 ) . any ( |w| {
857
+ w[ 0 ] == "-x=c++" || w[ 1 ] == "-x=c++" || w == & [ "-x" , "c++" ]
858
+ } ) ;
859
+
860
+ let wrapper_path = PathBuf :: from ( if is_cpp {
861
+ "__bindgen.cpp"
862
+ } else {
863
+ "__bindgen.c"
864
+ } ) ;
865
+
866
+ {
867
+ let mut wrapper_file = File :: create ( & wrapper_path) ?;
868
+ wrapper_file. write ( wrapper_contents. as_bytes ( ) ) ?;
869
+ }
870
+
871
+ let mut cmd = Command :: new ( & clang. path ) ;
872
+ cmd. arg ( "-save-temps" )
873
+ . arg ( "-c" )
874
+ . arg ( & wrapper_path) ;
875
+
876
+ for a in & self . options . clang_args {
877
+ cmd. arg ( a) ;
878
+ }
879
+
880
+ if cmd. spawn ( ) ?. wait ( ) ?. success ( ) {
881
+ Ok ( ( ) )
882
+ } else {
883
+ Err ( io:: Error :: new ( io:: ErrorKind :: Other ,
884
+ "clang exited with non-zero status" ) )
885
+ }
886
+ }
817
887
}
818
888
819
889
/// Configuration options for generated bindings.
0 commit comments