@@ -87,9 +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
+ use std:: iter;
91
92
use std:: io:: { self , Write } ;
92
- use std:: path:: Path ;
93
+ use std:: path:: { Path , PathBuf } ;
94
+ use std:: process:: Command ;
93
95
use std:: sync:: Arc ;
94
96
95
97
use syntax:: ast;
@@ -165,9 +167,12 @@ impl Default for CodegenConfig {
165
167
/// // Write the generated bindings to an output file.
166
168
/// try!(bindings.write_to_file("path/to/output.rs"));
167
169
/// ```
168
- #[ derive( Debug , Default ) ]
170
+ #[ derive( Debug , Default ) ]
169
171
pub struct Builder {
170
172
options : BindgenOptions ,
173
+ input_headers : Vec < String > ,
174
+ // Tuples of unsaved file contents of the form (name, contents).
175
+ input_header_contents : Vec < ( String , String ) > ,
171
176
}
172
177
173
178
/// Construct a new [`Builder`](./struct.Builder.html).
@@ -180,9 +185,9 @@ impl Builder {
180
185
pub fn command_line_flags ( & self ) -> Vec < String > {
181
186
let mut output_vector: Vec < String > = Vec :: new ( ) ;
182
187
183
- if let Some ( ref header) = self . options . input_header {
184
- //Positional argument 'header'
185
- output_vector. push ( header. clone ( ) . into ( ) ) ;
188
+ if let Some ( header) = self . input_headers . last ( ) . cloned ( ) {
189
+ // Positional argument 'header'
190
+ output_vector. push ( header) ;
186
191
}
187
192
188
193
self . options
@@ -412,16 +417,19 @@ impl Builder {
412
417
} )
413
418
. count ( ) ;
414
419
420
+ output_vector. push ( "--" . into ( ) ) ;
421
+
415
422
if !self . options . clang_args . is_empty ( ) {
416
- output_vector. push ( "--" . into ( ) ) ;
417
- self . options
418
- . clang_args
419
- . iter ( )
420
- . cloned ( )
421
- . map ( |item| {
422
- output_vector. push ( item) ;
423
- } )
424
- . count ( ) ;
423
+ output_vector. extend (
424
+ self . options
425
+ . clang_args
426
+ . iter ( )
427
+ . cloned ( )
428
+ ) ;
429
+ }
430
+
431
+ if self . input_headers . len ( ) > 1 {
432
+ output_vector. extend ( self . input_headers [ ..self . input_headers . len ( ) - 1 ] . iter ( ) . cloned ( ) ) ;
425
433
}
426
434
427
435
output_vector
@@ -450,21 +458,15 @@ impl Builder {
450
458
/// .unwrap();
451
459
/// ```
452
460
pub fn header < T : Into < String > > ( mut self , header : T ) -> Builder {
453
- if let Some ( prev_header) = self . options . input_header . take ( ) {
454
- self . options . clang_args . push ( "-include" . into ( ) ) ;
455
- self . options . clang_args . push ( prev_header) ;
456
- }
457
-
458
- let header = header. into ( ) ;
459
- self . options . input_header = Some ( header) ;
461
+ self . input_headers . push ( header. into ( ) ) ;
460
462
self
461
463
}
462
464
463
465
/// Add `contents` as an input C/C++ header named `name`.
464
466
///
465
467
/// The file `name` will be added to the clang arguments.
466
468
pub fn header_contents ( mut self , name : & str , contents : & str ) -> Builder {
467
- self . options . input_unsaved_files . push ( clang :: UnsavedFile :: new ( name, contents) ) ;
469
+ self . input_header_contents . push ( ( name. into ( ) , contents. into ( ) ) ) ;
468
470
self
469
471
}
470
472
@@ -794,9 +796,94 @@ impl Builder {
794
796
}
795
797
796
798
/// Generate the Rust bindings using the options built up thus far.
797
- pub fn generate < ' ctx > ( self ) -> Result < Bindings < ' ctx > , ( ) > {
799
+ pub fn generate < ' ctx > ( mut self ) -> Result < Bindings < ' ctx > , ( ) > {
800
+ self . options . input_header = self . input_headers . pop ( ) ;
801
+ self . options . clang_args . extend (
802
+ self . input_headers
803
+ . drain ( ..)
804
+ . flat_map ( |header| {
805
+ iter:: once ( "-include" . into ( ) )
806
+ . chain ( iter:: once ( header) )
807
+ } )
808
+ ) ;
809
+
810
+ self . options . input_unsaved_files . extend (
811
+ self . input_header_contents
812
+ . drain ( ..)
813
+ . map ( |( name, contents) | clang:: UnsavedFile :: new ( & name, & contents) )
814
+ ) ;
815
+
798
816
Bindings :: generate ( self . options , None )
799
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
+ }
800
887
}
801
888
802
889
/// Configuration options for generated bindings.
0 commit comments