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