1 use bindgen::{builder, Builder, CodegenConfig, EnumVariation, RustTarget, RUST_TARGET_STRINGS};
2 use clap::{App, Arg};
3 use std::fs::File;
4 use std::io::{self, stderr, Error, ErrorKind, Write};
5 use std::path::PathBuf;
6 use std::str::FromStr;
7 
8 /// Construct a new [`Builder`](./struct.Builder.html) from command line flags.
builder_from_flags<I>(args: I) -> Result<(Builder, Box<dyn io::Write>, bool), io::Error> where I: Iterator<Item = String>,9 pub fn builder_from_flags<I>(args: I) -> Result<(Builder, Box<dyn io::Write>, bool), io::Error>
10 where
11     I: Iterator<Item = String>,
12 {
13     let rust_target_help = format!(
14         "Version of the Rust compiler to target. Valid options are: {:?}. Defaults to {:?}.",
15         RUST_TARGET_STRINGS,
16         String::from(RustTarget::default())
17     );
18 
19     let matches = App::new("bindgen")
20         .version(option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"))
21         .about("Generates Rust bindings from C/C++ headers.")
22         .usage("bindgen [FLAGS] [OPTIONS] <header> -- <clang-args>...")
23         .args(&[
24             Arg::with_name("header")
25                 .help("C or C++ header file")
26                 .required(true),
27             Arg::with_name("default-enum-style")
28                 .long("default-enum-style")
29                 .help("The default style of code used to generate enums.")
30                 .value_name("variant")
31                 .default_value("consts")
32                 .possible_values(&[
33                     "consts",
34                     "moduleconsts",
35                     "bitfield",
36                     "rust",
37                     "rust_non_exhaustive",
38                 ])
39                 .multiple(false),
40             Arg::with_name("bitfield-enum")
41                 .long("bitfield-enum")
42                 .help(
43                     "Mark any enum whose name matches <regex> as a set of \
44                      bitfield flags.",
45                 )
46                 .value_name("regex")
47                 .takes_value(true)
48                 .multiple(true)
49                 .number_of_values(1),
50             Arg::with_name("rustified-enum")
51                 .long("rustified-enum")
52                 .help("Mark any enum whose name matches <regex> as a Rust enum.")
53                 .value_name("regex")
54                 .takes_value(true)
55                 .multiple(true)
56                 .number_of_values(1),
57             Arg::with_name("constified-enum")
58                 .long("constified-enum")
59                 .help(
60                     "Mark any enum whose name matches <regex> as a series of \
61                      constants.",
62                 )
63                 .value_name("regex")
64                 .takes_value(true)
65                 .multiple(true)
66                 .number_of_values(1),
67             Arg::with_name("constified-enum-module")
68                 .long("constified-enum-module")
69                 .help(
70                     "Mark any enum whose name matches <regex> as a module of \
71                      constants.",
72                 )
73                 .value_name("regex")
74                 .takes_value(true)
75                 .multiple(true)
76                 .number_of_values(1),
77             Arg::with_name("blacklist-type")
78                 .long("blacklist-type")
79                 .help("Mark <type> as hidden.")
80                 .value_name("type")
81                 .takes_value(true)
82                 .multiple(true)
83                 .number_of_values(1),
84             Arg::with_name("blacklist-function")
85                 .long("blacklist-function")
86                 .help("Mark <function> as hidden.")
87                 .value_name("function")
88                 .takes_value(true)
89                 .multiple(true)
90                 .number_of_values(1),
91             Arg::with_name("blacklist-item")
92                 .long("blacklist-item")
93                 .help("Mark <item> as hidden.")
94                 .value_name("item")
95                 .takes_value(true)
96                 .multiple(true)
97                 .number_of_values(1),
98             Arg::with_name("no-layout-tests")
99                 .long("no-layout-tests")
100                 .help("Avoid generating layout tests for any type."),
101             Arg::with_name("no-derive-copy")
102                 .long("no-derive-copy")
103                 .help("Avoid deriving Copy on any type."),
104             Arg::with_name("no-derive-debug")
105                 .long("no-derive-debug")
106                 .help("Avoid deriving Debug on any type."),
107             Arg::with_name("no-derive-default")
108                 .long("no-derive-default")
109                 .hidden(true)
110                 .help("Avoid deriving Default on any type."),
111             Arg::with_name("impl-debug").long("impl-debug").help(
112                 "Create Debug implementation, if it can not be derived \
113                  automatically.",
114             ),
115             Arg::with_name("impl-partialeq")
116                 .long("impl-partialeq")
117                 .help(
118                     "Create PartialEq implementation, if it can not be derived \
119                      automatically.",
120                 ),
121             Arg::with_name("with-derive-default")
122                 .long("with-derive-default")
123                 .help("Derive Default on any type."),
124             Arg::with_name("with-derive-hash")
125                 .long("with-derive-hash")
126                 .help("Derive hash on any type."),
127             Arg::with_name("with-derive-partialeq")
128                 .long("with-derive-partialeq")
129                 .help("Derive partialeq on any type."),
130             Arg::with_name("with-derive-partialord")
131                 .long("with-derive-partialord")
132                 .help("Derive partialord on any type."),
133             Arg::with_name("with-derive-eq")
134                 .long("with-derive-eq")
135                 .help(
136                     "Derive eq on any type. Enable this option also \
137                      enables --with-derive-partialeq",
138                 ),
139             Arg::with_name("with-derive-ord")
140                 .long("with-derive-ord")
141                 .help(
142                     "Derive ord on any type. Enable this option also \
143                      enables --with-derive-partialord",
144                 ),
145             Arg::with_name("no-doc-comments")
146                 .long("no-doc-comments")
147                 .help(
148                     "Avoid including doc comments in the output, see: \
149                      https://github.com/rust-lang/rust-bindgen/issues/426",
150                 ),
151             Arg::with_name("no-recursive-whitelist")
152                 .long("no-recursive-whitelist")
153                 .help(
154                     "Disable whitelisting types recursively. This will cause \
155                      bindgen to emit Rust code that won't compile! See the \
156                      `bindgen::Builder::whitelist_recursively` method's \
157                      documentation for details.",
158                 ),
159             Arg::with_name("objc-extern-crate")
160                 .long("objc-extern-crate")
161                 .help("Use extern crate instead of use for objc."),
162             Arg::with_name("generate-block")
163                 .long("generate-block")
164                 .help("Generate block signatures instead of void pointers."),
165             Arg::with_name("block-extern-crate")
166                 .long("block-extern-crate")
167                 .help("Use extern crate instead of use for block."),
168             Arg::with_name("distrust-clang-mangling")
169                 .long("distrust-clang-mangling")
170                 .help("Do not trust the libclang-provided mangling"),
171             Arg::with_name("builtins").long("builtins").help(
172                 "Output bindings for builtin definitions, e.g. \
173                  __builtin_va_list.",
174             ),
175             Arg::with_name("ctypes-prefix")
176                 .long("ctypes-prefix")
177                 .help(
178                     "Use the given prefix before raw types instead of \
179                      ::std::os::raw.",
180                 )
181                 .value_name("prefix")
182                 .takes_value(true),
183             Arg::with_name("time-phases")
184                 .long("time-phases")
185                 .help("Time the different bindgen phases and print to stderr"),
186             // All positional arguments after the end of options marker, `--`
187             Arg::with_name("clang-args").last(true).multiple(true),
188             Arg::with_name("emit-clang-ast")
189                 .long("emit-clang-ast")
190                 .help("Output the Clang AST for debugging purposes."),
191             Arg::with_name("emit-ir")
192                 .long("emit-ir")
193                 .help("Output our internal IR for debugging purposes."),
194             Arg::with_name("emit-ir-graphviz")
195                 .long("emit-ir-graphviz")
196                 .help("Dump graphviz dot file.")
197                 .value_name("path")
198                 .takes_value(true),
199             Arg::with_name("enable-cxx-namespaces")
200                 .long("enable-cxx-namespaces")
201                 .help("Enable support for C++ namespaces."),
202             Arg::with_name("disable-name-namespacing")
203                 .long("disable-name-namespacing")
204                 .help(
205                     "Disable namespacing via mangling, causing bindgen to \
206                      generate names like \"Baz\" instead of \"foo_bar_Baz\" \
207                      for an input name \"foo::bar::Baz\".",
208                 ),
209             Arg::with_name("ignore-functions")
210                 .long("ignore-functions")
211                 .help(
212                     "Do not generate bindings for functions or methods. This \
213                      is useful when you only care about struct layouts.",
214                 ),
215             Arg::with_name("generate")
216                 .long("generate")
217                 .help(
218                     "Generate only given items, split by commas. \
219                      Valid values are \"functions\",\"types\", \"vars\", \
220                      \"methods\", \"constructors\" and \"destructors\".",
221                 )
222                 .takes_value(true),
223             Arg::with_name("ignore-methods")
224                 .long("ignore-methods")
225                 .help("Do not generate bindings for methods."),
226             Arg::with_name("no-convert-floats")
227                 .long("no-convert-floats")
228                 .help("Do not automatically convert floats to f32/f64."),
229             Arg::with_name("no-prepend-enum-name")
230                 .long("no-prepend-enum-name")
231                 .help("Do not prepend the enum name to bitfield or constant variants."),
232             Arg::with_name("no-include-path-detection")
233                 .long("no-include-path-detection")
234                 .help("Do not try to detect default include paths"),
235             Arg::with_name("unstable-rust")
236                 .long("unstable-rust")
237                 .help("Generate unstable Rust code (deprecated; use --rust-target instead).")
238                 .multiple(true), // FIXME: Pass legacy test suite
239             Arg::with_name("opaque-type")
240                 .long("opaque-type")
241                 .help("Mark <type> as opaque.")
242                 .value_name("type")
243                 .takes_value(true)
244                 .multiple(true)
245                 .number_of_values(1),
246             Arg::with_name("output")
247                 .short("o")
248                 .long("output")
249                 .help("Write Rust bindings to <output>.")
250                 .takes_value(true),
251             Arg::with_name("raw-line")
252                 .long("raw-line")
253                 .help("Add a raw line of Rust code at the beginning of output.")
254                 .takes_value(true)
255                 .multiple(true)
256                 .number_of_values(1),
257             Arg::with_name("rust-target")
258                 .long("rust-target")
259                 .help(&rust_target_help)
260                 .takes_value(true),
261             Arg::with_name("use-core")
262                 .long("use-core")
263                 .help("Use types from Rust core instead of std."),
264             Arg::with_name("conservative-inline-namespaces")
265                 .long("conservative-inline-namespaces")
266                 .help(
267                     "Conservatively generate inline namespaces to avoid name \
268                      conflicts.",
269                 ),
270             Arg::with_name("use-msvc-mangling")
271                 .long("use-msvc-mangling")
272                 .help("MSVC C++ ABI mangling. DEPRECATED: Has no effect."),
273             Arg::with_name("whitelist-function")
274                 .long("whitelist-function")
275                 .help(
276                     "Whitelist all the free-standing functions matching \
277                      <regex>. Other non-whitelisted functions will not be \
278                      generated.",
279                 )
280                 .value_name("regex")
281                 .takes_value(true)
282                 .multiple(true)
283                 .number_of_values(1),
284             Arg::with_name("generate-inline-functions")
285                 .long("generate-inline-functions")
286                 .help("Generate inline functions."),
287             Arg::with_name("whitelist-type")
288                 .long("whitelist-type")
289                 .help(
290                     "Only generate types matching <regex>. Other non-whitelisted types will \
291                      not be generated.",
292                 )
293                 .value_name("regex")
294                 .takes_value(true)
295                 .multiple(true)
296                 .number_of_values(1),
297             Arg::with_name("whitelist-var")
298                 .long("whitelist-var")
299                 .help(
300                     "Whitelist all the free-standing variables matching \
301                      <regex>. Other non-whitelisted variables will not be \
302                      generated.",
303                 )
304                 .value_name("regex")
305                 .takes_value(true)
306                 .multiple(true)
307                 .number_of_values(1),
308             Arg::with_name("verbose")
309                 .long("verbose")
310                 .help("Print verbose error messages."),
311             Arg::with_name("dump-preprocessed-input")
312                 .long("dump-preprocessed-input")
313                 .help(
314                     "Preprocess and dump the input header files to disk. \
315                      Useful when debugging bindgen, using C-Reduce, or when \
316                      filing issues. The resulting file will be named \
317                      something like `__bindgen.i` or `__bindgen.ii`.",
318                 ),
319             Arg::with_name("no-record-matches")
320                 .long("no-record-matches")
321                 .help(
322                     "Do not record matching items in the regex sets. \
323                      This disables reporting of unused items.",
324                 ),
325             Arg::with_name("no-rustfmt-bindings")
326                 .long("no-rustfmt-bindings")
327                 .help("Do not format the generated bindings with rustfmt."),
328             Arg::with_name("rustfmt-bindings")
329                 .long("rustfmt-bindings")
330                 .help(
331                     "Format the generated bindings with rustfmt. DEPRECATED: \
332                      --rustfmt-bindings is now enabled by default. Disable \
333                      with --no-rustfmt-bindings.",
334                 ),
335             Arg::with_name("rustfmt-configuration-file")
336                 .long("rustfmt-configuration-file")
337                 .help(
338                     "The absolute path to the rustfmt configuration file. \
339                      The configuration file will be used for formatting the bindings. \
340                      This parameter is incompatible with --no-rustfmt-bindings.",
341                 )
342                 .value_name("path")
343                 .takes_value(true)
344                 .multiple(false)
345                 .number_of_values(1),
346             Arg::with_name("no-partialeq")
347                 .long("no-partialeq")
348                 .help("Avoid deriving PartialEq for types matching <regex>.")
349                 .value_name("regex")
350                 .takes_value(true)
351                 .multiple(true)
352                 .number_of_values(1),
353             Arg::with_name("no-copy")
354                 .long("no-copy")
355                 .help("Avoid deriving Copy for types matching <regex>.")
356                 .value_name("regex")
357                 .takes_value(true)
358                 .multiple(true)
359                 .number_of_values(1),
360             Arg::with_name("no-hash")
361                 .long("no-hash")
362                 .help("Avoid deriving Hash for types matching <regex>.")
363                 .value_name("regex")
364                 .takes_value(true)
365                 .multiple(true)
366                 .number_of_values(1),
367             Arg::with_name("enable-function-attribute-detection")
368                 .long("enable-function-attribute-detection")
369                 .help(
370                     "Enables detecting unexposed attributes in functions (slow).
371                        Used to generate #[must_use] annotations.",
372                 ),
373             Arg::with_name("use-array-pointers-in-arguments")
374                 .long("use-array-pointers-in-arguments")
375                 .help("Use `*const [T; size]` instead of `*const T` for C arrays"),
376         ]) // .args()
377         .get_matches_from(args);
378 
379     let mut builder = builder();
380 
381     if let Some(header) = matches.value_of("header") {
382         builder = builder.header(header);
383     } else {
384         return Err(Error::new(ErrorKind::Other, "Header not found"));
385     }
386 
387     if matches.is_present("unstable-rust") {
388         builder = builder.rust_target(RustTarget::Nightly);
389         writeln!(
390             &mut stderr(),
391             "warning: the `--unstable-rust` option is deprecated"
392         )
393         .expect("Unable to write error message");
394     }
395 
396     if let Some(rust_target) = matches.value_of("rust-target") {
397         builder = builder.rust_target(RustTarget::from_str(rust_target)?);
398     }
399 
400     if let Some(variant) = matches.value_of("default-enum-style") {
401         builder = builder.default_enum_style(EnumVariation::from_str(variant)?)
402     }
403 
404     if let Some(bitfields) = matches.values_of("bitfield-enum") {
405         for regex in bitfields {
406             builder = builder.bitfield_enum(regex);
407         }
408     }
409 
410     if let Some(rustifieds) = matches.values_of("rustified-enum") {
411         for regex in rustifieds {
412             builder = builder.rustified_enum(regex);
413         }
414     }
415 
416     if let Some(bitfields) = matches.values_of("constified-enum") {
417         for regex in bitfields {
418             builder = builder.constified_enum(regex);
419         }
420     }
421 
422     if let Some(constified_mods) = matches.values_of("constified-enum-module") {
423         for regex in constified_mods {
424             builder = builder.constified_enum_module(regex);
425         }
426     }
427     if let Some(hidden_types) = matches.values_of("blacklist-type") {
428         for ty in hidden_types {
429             builder = builder.blacklist_type(ty);
430         }
431     }
432 
433     if let Some(hidden_functions) = matches.values_of("blacklist-function") {
434         for fun in hidden_functions {
435             builder = builder.blacklist_function(fun);
436         }
437     }
438 
439     if let Some(hidden_identifiers) = matches.values_of("blacklist-item") {
440         for id in hidden_identifiers {
441             builder = builder.blacklist_item(id);
442         }
443     }
444 
445     if matches.is_present("builtins") {
446         builder = builder.emit_builtins();
447     }
448 
449     if matches.is_present("no-layout-tests") {
450         builder = builder.layout_tests(false);
451     }
452 
453     if matches.is_present("no-derive-copy") {
454         builder = builder.derive_copy(false);
455     }
456 
457     if matches.is_present("no-derive-debug") {
458         builder = builder.derive_debug(false);
459     }
460 
461     if matches.is_present("impl-debug") {
462         builder = builder.impl_debug(true);
463     }
464 
465     if matches.is_present("impl-partialeq") {
466         builder = builder.impl_partialeq(true);
467     }
468 
469     if matches.is_present("with-derive-default") {
470         builder = builder.derive_default(true);
471     }
472 
473     if matches.is_present("with-derive-hash") {
474         builder = builder.derive_hash(true);
475     }
476 
477     if matches.is_present("with-derive-partialeq") {
478         builder = builder.derive_partialeq(true);
479     }
480 
481     if matches.is_present("with-derive-partialord") {
482         builder = builder.derive_partialord(true);
483     }
484 
485     if matches.is_present("with-derive-eq") {
486         builder = builder.derive_eq(true);
487     }
488 
489     if matches.is_present("with-derive-ord") {
490         builder = builder.derive_ord(true);
491     }
492 
493     if matches.is_present("no-derive-default") {
494         builder = builder.derive_default(false);
495     }
496 
497     if matches.is_present("no-prepend-enum-name") {
498         builder = builder.prepend_enum_name(false);
499     }
500 
501     if matches.is_present("no-include-path-detection") {
502         builder = builder.detect_include_paths(false);
503     }
504 
505     if matches.is_present("time-phases") {
506         builder = builder.time_phases(true);
507     }
508 
509     if matches.is_present("use-array-pointers-in-arguments") {
510         builder = builder.array_pointers_in_arguments(true);
511     }
512 
513     if let Some(prefix) = matches.value_of("ctypes-prefix") {
514         builder = builder.ctypes_prefix(prefix);
515     }
516 
517     if let Some(what_to_generate) = matches.value_of("generate") {
518         let mut config = CodegenConfig::empty();
519         for what in what_to_generate.split(",") {
520             match what {
521                 "functions" => config.insert(CodegenConfig::FUNCTIONS),
522                 "types" => config.insert(CodegenConfig::TYPES),
523                 "vars" => config.insert(CodegenConfig::VARS),
524                 "methods" => config.insert(CodegenConfig::METHODS),
525                 "constructors" => config.insert(CodegenConfig::CONSTRUCTORS),
526                 "destructors" => config.insert(CodegenConfig::DESTRUCTORS),
527                 otherwise => {
528                     return Err(Error::new(
529                         ErrorKind::Other,
530                         format!("Unknown generate item: {}", otherwise),
531                     ));
532                 }
533             }
534         }
535         builder = builder.with_codegen_config(config);
536     }
537 
538     if matches.is_present("emit-clang-ast") {
539         builder = builder.emit_clang_ast();
540     }
541 
542     if matches.is_present("emit-ir") {
543         builder = builder.emit_ir();
544     }
545 
546     if let Some(path) = matches.value_of("emit-ir-graphviz") {
547         builder = builder.emit_ir_graphviz(path);
548     }
549 
550     if matches.is_present("enable-cxx-namespaces") {
551         builder = builder.enable_cxx_namespaces();
552     }
553 
554     if matches.is_present("enable-function-attribute-detection") {
555         builder = builder.enable_function_attribute_detection();
556     }
557 
558     if matches.is_present("disable-name-namespacing") {
559         builder = builder.disable_name_namespacing();
560     }
561 
562     if matches.is_present("ignore-functions") {
563         builder = builder.ignore_functions();
564     }
565 
566     if matches.is_present("ignore-methods") {
567         builder = builder.ignore_methods();
568     }
569 
570     if matches.is_present("no-convert-floats") {
571         builder = builder.no_convert_floats();
572     }
573 
574     if matches.is_present("no-doc-comments") {
575         builder = builder.generate_comments(false);
576     }
577 
578     if matches.is_present("no-recursive-whitelist") {
579         builder = builder.whitelist_recursively(false);
580     }
581 
582     if matches.is_present("objc-extern-crate") {
583         builder = builder.objc_extern_crate(true);
584     }
585 
586     if matches.is_present("generate-block") {
587         builder = builder.generate_block(true);
588     }
589 
590     if matches.is_present("block-extern-crate") {
591         builder = builder.block_extern_crate(true);
592     }
593 
594     if let Some(opaque_types) = matches.values_of("opaque-type") {
595         for ty in opaque_types {
596             builder = builder.opaque_type(ty);
597         }
598     }
599 
600     if let Some(lines) = matches.values_of("raw-line") {
601         for line in lines {
602             builder = builder.raw_line(line);
603         }
604     }
605 
606     if matches.is_present("use-core") {
607         builder = builder.use_core();
608     }
609 
610     if matches.is_present("distrust-clang-mangling") {
611         builder = builder.trust_clang_mangling(false);
612     }
613 
614     if matches.is_present("conservative-inline-namespaces") {
615         builder = builder.conservative_inline_namespaces();
616     }
617 
618     if matches.is_present("generate-inline-functions") {
619         builder = builder.generate_inline_functions(true);
620     }
621 
622     if let Some(whitelist) = matches.values_of("whitelist-function") {
623         for regex in whitelist {
624             builder = builder.whitelist_function(regex);
625         }
626     }
627 
628     if let Some(whitelist) = matches.values_of("whitelist-type") {
629         for regex in whitelist {
630             builder = builder.whitelist_type(regex);
631         }
632     }
633 
634     if let Some(whitelist) = matches.values_of("whitelist-var") {
635         for regex in whitelist {
636             builder = builder.whitelist_var(regex);
637         }
638     }
639 
640     if let Some(args) = matches.values_of("clang-args") {
641         for arg in args {
642             builder = builder.clang_arg(arg);
643         }
644     }
645 
646     let output = if let Some(path) = matches.value_of("output") {
647         let file = File::create(path)?;
648         Box::new(io::BufWriter::new(file)) as Box<dyn io::Write>
649     } else {
650         Box::new(io::BufWriter::new(io::stdout())) as Box<dyn io::Write>
651     };
652 
653     if matches.is_present("dump-preprocessed-input") {
654         builder.dump_preprocessed_input()?;
655     }
656 
657     if matches.is_present("no-record-matches") {
658         builder = builder.record_matches(false);
659     }
660 
661     let no_rustfmt_bindings = matches.is_present("no-rustfmt-bindings");
662     if no_rustfmt_bindings {
663         builder = builder.rustfmt_bindings(false);
664     }
665 
666     if let Some(path_str) = matches.value_of("rustfmt-configuration-file") {
667         let path = PathBuf::from(path_str);
668 
669         if no_rustfmt_bindings {
670             return Err(Error::new(
671                 ErrorKind::Other,
672                 "Cannot supply both --rustfmt-configuration-file and --no-rustfmt-bindings",
673             ));
674         }
675 
676         if !path.is_absolute() {
677             return Err(Error::new(
678                 ErrorKind::Other,
679                 "--rustfmt-configuration--file needs to be an absolute path!",
680             ));
681         }
682 
683         if path.to_str().is_none() {
684             return Err(Error::new(
685                 ErrorKind::Other,
686                 "--rustfmt-configuration-file contains non-valid UTF8 characters.",
687             ));
688         }
689 
690         builder = builder.rustfmt_configuration_file(Some(path));
691     }
692 
693     if let Some(no_partialeq) = matches.values_of("no-partialeq") {
694         for regex in no_partialeq {
695             builder = builder.no_partialeq(regex);
696         }
697     }
698 
699     if let Some(no_copy) = matches.values_of("no-copy") {
700         for regex in no_copy {
701             builder = builder.no_copy(regex);
702         }
703     }
704 
705     if let Some(no_hash) = matches.values_of("no-hash") {
706         for regex in no_hash {
707             builder = builder.no_hash(regex);
708         }
709     }
710 
711     let verbose = matches.is_present("verbose");
712 
713     Ok((builder, output, verbose))
714 }
715