1//! Generate Rust bindings for C and C++ libraries.
2//!
3//! Provide a C/C++ header file, receive Rust FFI code to call into C/C++
4//! functions and use types defined in the header.
5//!
6//! See the [`Builder`](./struct.Builder.html) struct for usage.
7//!
8//! See the [Users Guide](https://rust-lang.github.io/rust-bindgen/) for
9//! additional documentation.
10#![deny(missing_docs)]
11#![deny(unused_extern_crates)]
12#![deny(clippy::disallowed_methods)]
13// To avoid rather annoying warnings when matching with CXCursor_xxx as a
14// constant.
15#![allow(non_upper_case_globals)]
16// `quote!` nests quite deeply.
17#![recursion_limit = "128"]
18
19#[macro_use]
20extern crate bitflags;
21#[macro_use]
22extern crate lazy_static;
23#[macro_use]
24extern crate quote;
25
26#[cfg(feature = "logging")]
27#[macro_use]
28extern crate log;
29
30#[cfg(not(feature = "logging"))]
31#[macro_use]
32mod log_stubs;
33
34#[macro_use]
35mod extra_assertions;
36
37mod codegen;
38mod deps;
39mod options;
40mod time;
41
42pub mod callbacks;
43
44mod clang;
45#[cfg(feature = "experimental")]
46mod diagnostics;
47mod features;
48mod ir;
49mod parse;
50mod regex_set;
51
52pub use codegen::{
53 AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
54};
55#[cfg(feature = "__cli")]
56pub use features::RUST_TARGET_STRINGS;
57pub use features::{RustTarget, LATEST_STABLE_RUST};
58pub use ir::annotations::FieldVisibilityKind;
59pub use ir::function::Abi;
60pub use regex_set::RegexSet;
61
62use codegen::CodegenError;
63use features::RustFeatures;
64use ir::comment;
65use ir::context::{BindgenContext, ItemId};
66use ir::item::Item;
67use options::BindgenOptions;
68use parse::ParseError;
69
70use std::borrow::Cow;
71use std::collections::hash_map::Entry;
72use std::env;
73use std::ffi::OsStr;
74use std::fs::{File, OpenOptions};
75use std::io::{self, Write};
76use std::path::{Path, PathBuf};
77use std::process::{Command, Stdio};
78use std::rc::Rc;
79use std::str::FromStr;
80
81// Some convenient typedefs for a fast hash map and hash set.
82type HashMap<K, V> = rustc_hash::FxHashMap<K, V>;
83type HashSet<K> = rustc_hash::FxHashSet<K>;
84
85/// Default prefix for the anon fields.
86pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
87
88const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern";
89
90fn file_is_cpp(name_file: &str) -> bool {
91 name_file.ends_with(".hpp") ||
92 name_file.ends_with(".hxx") ||
93 name_file.ends_with(".hh") ||
94 name_file.ends_with(".h++")
95}
96
97fn args_are_cpp(clang_args: &[Box<str>]) -> bool {
98 for w: &[Box] in clang_args.windows(size:2) {
99 if w[0].as_ref() == "-xc++" || w[1].as_ref() == "-xc++" {
100 return true;
101 }
102 if w[0].as_ref() == "-x" && w[1].as_ref() == "c++" {
103 return true;
104 }
105 if w[0].as_ref() == "-include" && file_is_cpp(name_file:w[1].as_ref()) {
106 return true;
107 }
108 }
109 false
110}
111
112bitflags! {
113 /// A type used to indicate which kind of items we have to generate.
114 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
115 pub struct CodegenConfig: u32 {
116 /// Whether to generate functions.
117 const FUNCTIONS = 1 << 0;
118 /// Whether to generate types.
119 const TYPES = 1 << 1;
120 /// Whether to generate constants.
121 const VARS = 1 << 2;
122 /// Whether to generate methods.
123 const METHODS = 1 << 3;
124 /// Whether to generate constructors
125 const CONSTRUCTORS = 1 << 4;
126 /// Whether to generate destructors.
127 const DESTRUCTORS = 1 << 5;
128 }
129}
130
131impl CodegenConfig {
132 /// Returns true if functions should be generated.
133 pub fn functions(self) -> bool {
134 self.contains(CodegenConfig::FUNCTIONS)
135 }
136
137 /// Returns true if types should be generated.
138 pub fn types(self) -> bool {
139 self.contains(CodegenConfig::TYPES)
140 }
141
142 /// Returns true if constants should be generated.
143 pub fn vars(self) -> bool {
144 self.contains(CodegenConfig::VARS)
145 }
146
147 /// Returns true if methds should be generated.
148 pub fn methods(self) -> bool {
149 self.contains(CodegenConfig::METHODS)
150 }
151
152 /// Returns true if constructors should be generated.
153 pub fn constructors(self) -> bool {
154 self.contains(CodegenConfig::CONSTRUCTORS)
155 }
156
157 /// Returns true if destructors should be generated.
158 pub fn destructors(self) -> bool {
159 self.contains(CodegenConfig::DESTRUCTORS)
160 }
161}
162
163impl Default for CodegenConfig {
164 fn default() -> Self {
165 CodegenConfig::all()
166 }
167}
168
169/// Formatting tools that can be used to format the bindings
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171#[non_exhaustive]
172pub enum Formatter {
173 /// Do not format the bindings.
174 None,
175 /// Use `rustfmt` to format the bindings.
176 Rustfmt,
177 #[cfg(feature = "prettyplease")]
178 /// Use `prettyplease` to format the bindings.
179 Prettyplease,
180}
181
182impl Default for Formatter {
183 fn default() -> Self {
184 Self::Rustfmt
185 }
186}
187
188impl FromStr for Formatter {
189 type Err = String;
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 match s {
193 "none" => Ok(Self::None),
194 "rustfmt" => Ok(Self::Rustfmt),
195 #[cfg(feature = "prettyplease")]
196 "prettyplease" => Ok(Self::Prettyplease),
197 _ => Err(format!("`{}` is not a valid formatter", s)),
198 }
199 }
200}
201
202impl std::fmt::Display for Formatter {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 let s: &str = match self {
205 Self::None => "none",
206 Self::Rustfmt => "rustfmt",
207 #[cfg(feature = "prettyplease")]
208 Self::Prettyplease => "prettyplease",
209 };
210
211 s.fmt(f)
212 }
213}
214
215/// Configure and generate Rust bindings for a C/C++ header.
216///
217/// This is the main entry point to the library.
218///
219/// ```ignore
220/// use bindgen::builder;
221///
222/// // Configure and generate bindings.
223/// let bindings = builder().header("path/to/input/header")
224/// .allowlist_type("SomeCoolClass")
225/// .allowlist_function("do_some_cool_thing")
226/// .generate()?;
227///
228/// // Write the generated bindings to an output file.
229/// bindings.write_to_file("path/to/output.rs")?;
230/// ```
231///
232/// # Enums
233///
234/// Bindgen can map C/C++ enums into Rust in different ways. The way bindgen maps enums depends on
235/// the pattern passed to several methods:
236///
237/// 1. [`constified_enum_module()`](#method.constified_enum_module)
238/// 2. [`bitfield_enum()`](#method.bitfield_enum)
239/// 3. [`newtype_enum()`](#method.newtype_enum)
240/// 4. [`rustified_enum()`](#method.rustified_enum)
241///
242/// For each C enum, bindgen tries to match the pattern in the following order:
243///
244/// 1. Constified enum module
245/// 2. Bitfield enum
246/// 3. Newtype enum
247/// 4. Rustified enum
248///
249/// If none of the above patterns match, then bindgen will generate a set of Rust constants.
250///
251/// # Clang arguments
252///
253/// Extra arguments can be passed to with clang:
254/// 1. [`clang_arg()`](#method.clang_arg): takes a single argument
255/// 2. [`clang_args()`](#method.clang_args): takes an iterator of arguments
256/// 3. `BINDGEN_EXTRA_CLANG_ARGS` environment variable: whitespace separate
257/// environment variable of arguments
258///
259/// Clang arguments specific to your crate should be added via the
260/// `clang_arg()`/`clang_args()` methods.
261///
262/// End-users of the crate may need to set the `BINDGEN_EXTRA_CLANG_ARGS` environment variable to
263/// add additional arguments. For example, to build against a different sysroot a user could set
264/// `BINDGEN_EXTRA_CLANG_ARGS` to `--sysroot=/path/to/sysroot`.
265///
266/// # Regular expression arguments
267///
268/// Some [`Builder`] methods, such as `allowlist_*` and `blocklist_*`, allow regular
269/// expressions as arguments. These regular expressions will be enclosed in parentheses and
270/// anchored with `^` and `$`. So, if the argument passed is `<regex>`, the regular expression to be
271/// stored will be `^(<regex>)$`.
272///
273/// As a consequence, regular expressions passed to `bindgen` will try to match the whole name of
274/// an item instead of a section of it, which means that to match any items with the prefix
275/// `prefix`, the `prefix.*` regular expression must be used.
276///
277/// Certain methods, like [`Builder::allowlist_function`], use regular expressions over function
278/// names. To match C++ methods, prefix the name of the type where they belong, followed by an
279/// underscore. So, if the type `Foo` has a method `bar`, it can be matched with the `Foo_bar`
280/// regular expression.
281///
282/// Additionally, Objective-C interfaces can be matched by prefixing the regular expression with
283/// `I`. For example, the `IFoo` regular expression matches the `Foo` interface, and the `IFoo_foo`
284/// regular expression matches the `foo` method of the `Foo` interface.
285///
286/// Releases of `bindgen` with a version lesser or equal to `0.62.0` used to accept the wildcard
287/// pattern `*` as a valid regular expression. This behavior has been deprecated, and the `.*`
288/// regular expression must be used instead.
289#[derive(Debug, Default, Clone)]
290pub struct Builder {
291 options: BindgenOptions,
292}
293
294/// Construct a new [`Builder`](./struct.Builder.html).
295pub fn builder() -> Builder {
296 Default::default()
297}
298
299fn get_extra_clang_args(
300 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
301) -> Vec<String> {
302 // Add any extra arguments from the environment to the clang command line.
303 let extra_clang_args: String = match get_target_dependent_env_var(
304 parse_callbacks,
305 var:"BINDGEN_EXTRA_CLANG_ARGS",
306 ) {
307 None => return vec![],
308 Some(s: String) => s,
309 };
310
311 // Try to parse it with shell quoting. If we fail, make it one single big argument.
312 if let Some(strings: Vec) = shlex::split(&extra_clang_args) {
313 return strings;
314 }
315 vec![extra_clang_args]
316}
317
318impl Builder {
319 /// Generate the Rust bindings using the options built up thus far.
320 pub fn generate(mut self) -> Result<Bindings, BindgenError> {
321 // Add any extra arguments from the environment to the clang command line.
322 self.options.clang_args.extend(
323 get_extra_clang_args(&self.options.parse_callbacks)
324 .into_iter()
325 .map(String::into_boxed_str),
326 );
327
328 for header in &self.options.input_headers {
329 self.options
330 .for_each_callback(|cb| cb.header_file(header.as_ref()));
331 }
332
333 // Transform input headers to arguments on the clang command line.
334 self.options.clang_args.extend(
335 self.options.input_headers
336 [..self.options.input_headers.len().saturating_sub(1)]
337 .iter()
338 .flat_map(|header| ["-include".into(), header.clone()]),
339 );
340
341 let input_unsaved_files =
342 std::mem::take(&mut self.options.input_header_contents)
343 .into_iter()
344 .map(|(name, contents)| {
345 clang::UnsavedFile::new(name.as_ref(), contents.as_ref())
346 })
347 .collect::<Vec<_>>();
348
349 Bindings::generate(self.options, input_unsaved_files)
350 }
351
352 /// Preprocess and dump the input header files to disk.
353 ///
354 /// This is useful when debugging bindgen, using C-Reduce, or when filing
355 /// issues. The resulting file will be named something like `__bindgen.i` or
356 /// `__bindgen.ii`
357 pub fn dump_preprocessed_input(&self) -> io::Result<()> {
358 let clang =
359 clang_sys::support::Clang::find(None, &[]).ok_or_else(|| {
360 io::Error::new(
361 io::ErrorKind::Other,
362 "Cannot find clang executable",
363 )
364 })?;
365
366 // The contents of a wrapper file that includes all the input header
367 // files.
368 let mut wrapper_contents = String::new();
369
370 // Whether we are working with C or C++ inputs.
371 let mut is_cpp = args_are_cpp(&self.options.clang_args);
372
373 // For each input header, add `#include "$header"`.
374 for header in &self.options.input_headers {
375 is_cpp |= file_is_cpp(header);
376
377 wrapper_contents.push_str("#include \"");
378 wrapper_contents.push_str(header);
379 wrapper_contents.push_str("\"\n");
380 }
381
382 // For each input header content, add a prefix line of `#line 0 "$name"`
383 // followed by the contents.
384 for (name, contents) in &self.options.input_header_contents {
385 is_cpp |= file_is_cpp(name);
386
387 wrapper_contents.push_str("#line 0 \"");
388 wrapper_contents.push_str(name);
389 wrapper_contents.push_str("\"\n");
390 wrapper_contents.push_str(contents);
391 }
392
393 let wrapper_path = PathBuf::from(if is_cpp {
394 "__bindgen.cpp"
395 } else {
396 "__bindgen.c"
397 });
398
399 {
400 let mut wrapper_file = File::create(&wrapper_path)?;
401 wrapper_file.write_all(wrapper_contents.as_bytes())?;
402 }
403
404 let mut cmd = Command::new(clang.path);
405 cmd.arg("-save-temps")
406 .arg("-E")
407 .arg("-C")
408 .arg("-c")
409 .arg(&wrapper_path)
410 .stdout(Stdio::piped());
411
412 for a in &self.options.clang_args {
413 cmd.arg(a.as_ref());
414 }
415
416 for a in get_extra_clang_args(&self.options.parse_callbacks) {
417 cmd.arg(a);
418 }
419
420 let mut child = cmd.spawn()?;
421
422 let mut preprocessed = child.stdout.take().unwrap();
423 let mut file = File::create(if is_cpp {
424 "__bindgen.ii"
425 } else {
426 "__bindgen.i"
427 })?;
428 io::copy(&mut preprocessed, &mut file)?;
429
430 if child.wait()?.success() {
431 Ok(())
432 } else {
433 Err(io::Error::new(
434 io::ErrorKind::Other,
435 "clang exited with non-zero status",
436 ))
437 }
438 }
439}
440
441impl BindgenOptions {
442 fn build(&mut self) {
443 const REGEX_SETS_LEN: usize = 29;
444
445 let regex_sets: [_; REGEX_SETS_LEN] = [
446 &mut self.blocklisted_types,
447 &mut self.blocklisted_functions,
448 &mut self.blocklisted_items,
449 &mut self.blocklisted_files,
450 &mut self.blocklisted_vars,
451 &mut self.opaque_types,
452 &mut self.allowlisted_vars,
453 &mut self.allowlisted_types,
454 &mut self.allowlisted_functions,
455 &mut self.allowlisted_files,
456 &mut self.allowlisted_items,
457 &mut self.bitfield_enums,
458 &mut self.constified_enums,
459 &mut self.constified_enum_modules,
460 &mut self.newtype_enums,
461 &mut self.newtype_global_enums,
462 &mut self.rustified_enums,
463 &mut self.rustified_non_exhaustive_enums,
464 &mut self.type_alias,
465 &mut self.new_type_alias,
466 &mut self.new_type_alias_deref,
467 &mut self.bindgen_wrapper_union,
468 &mut self.manually_drop_union,
469 &mut self.no_partialeq_types,
470 &mut self.no_copy_types,
471 &mut self.no_debug_types,
472 &mut self.no_default_types,
473 &mut self.no_hash_types,
474 &mut self.must_use_types,
475 ];
476
477 let record_matches = self.record_matches;
478 #[cfg(feature = "experimental")]
479 {
480 let sets_len = REGEX_SETS_LEN + self.abi_overrides.len();
481 let names = if self.emit_diagnostics {
482 <[&str; REGEX_SETS_LEN]>::into_iter([
483 "--blocklist-type",
484 "--blocklist-function",
485 "--blocklist-item",
486 "--blocklist-file",
487 "--blocklist-var",
488 "--opaque-type",
489 "--allowlist-type",
490 "--allowlist-function",
491 "--allowlist-var",
492 "--allowlist-file",
493 "--allowlist-item",
494 "--bitfield-enum",
495 "--newtype-enum",
496 "--newtype-global-enum",
497 "--rustified-enum",
498 "--rustified-enum-non-exhaustive",
499 "--constified-enum-module",
500 "--constified-enum",
501 "--type-alias",
502 "--new-type-alias",
503 "--new-type-alias-deref",
504 "--bindgen-wrapper-union",
505 "--manually-drop-union",
506 "--no-partialeq",
507 "--no-copy",
508 "--no-debug",
509 "--no-default",
510 "--no-hash",
511 "--must-use",
512 ])
513 .chain((0..self.abi_overrides.len()).map(|_| "--override-abi"))
514 .map(Some)
515 .collect()
516 } else {
517 vec![None; sets_len]
518 };
519
520 for (regex_set, name) in
521 self.abi_overrides.values_mut().chain(regex_sets).zip(names)
522 {
523 regex_set.build_with_diagnostics(record_matches, name);
524 }
525 }
526 #[cfg(not(feature = "experimental"))]
527 for regex_set in self.abi_overrides.values_mut().chain(regex_sets) {
528 regex_set.build(record_matches);
529 }
530
531 let rust_target = self.rust_target;
532 #[allow(deprecated)]
533 if rust_target <= RustTarget::Stable_1_30 {
534 deprecated_target_diagnostic(rust_target, self);
535 }
536
537 // Disable `untagged_union` if the target does not support it.
538 if !self.rust_features.untagged_union {
539 self.untagged_union = false;
540 }
541 }
542
543 /// Update rust target version
544 pub fn set_rust_target(&mut self, rust_target: RustTarget) {
545 self.rust_target = rust_target;
546
547 // Keep rust_features synced with rust_target
548 self.rust_features = rust_target.into();
549 }
550
551 /// Get features supported by target Rust version
552 pub fn rust_features(&self) -> RustFeatures {
553 self.rust_features
554 }
555
556 fn last_callback<T>(
557 &self,
558 f: impl Fn(&dyn callbacks::ParseCallbacks) -> Option<T>,
559 ) -> Option<T> {
560 self.parse_callbacks
561 .iter()
562 .filter_map(|cb| f(cb.as_ref()))
563 .last()
564 }
565
566 fn all_callbacks<T>(
567 &self,
568 f: impl Fn(&dyn callbacks::ParseCallbacks) -> Vec<T>,
569 ) -> Vec<T> {
570 self.parse_callbacks
571 .iter()
572 .flat_map(|cb| f(cb.as_ref()))
573 .collect()
574 }
575
576 fn for_each_callback(&self, f: impl Fn(&dyn callbacks::ParseCallbacks)) {
577 self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref()));
578 }
579
580 fn process_comment(&self, comment: &str) -> String {
581 let comment = comment::preprocess(comment);
582 self.parse_callbacks
583 .last()
584 .and_then(|cb| cb.process_comment(&comment))
585 .unwrap_or(comment)
586 }
587}
588
589fn deprecated_target_diagnostic(target: RustTarget, _options: &BindgenOptions) {
590 warn!("The {} Rust target is deprecated. If you have a need to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", target);
591
592 #[cfg(feature = "experimental")]
593 if _options.emit_diagnostics {
594 use crate::diagnostics::{Diagnostic, Level};
595
596 let mut diagnostic = Diagnostic::default();
597 diagnostic.with_title(
598 format!("The {} Rust target is deprecated.", target),
599 Level::Warn,
600 );
601 diagnostic.add_annotation(
602 "This Rust target was passed to `--rust-target`",
603 Level::Info,
604 );
605 diagnostic.add_annotation("If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", Level::Help);
606 diagnostic.display();
607 }
608}
609
610#[cfg(feature = "runtime")]
611fn ensure_libclang_is_loaded() {
612 if clang_sys::is_loaded() {
613 return;
614 }
615
616 // XXX (issue #350): Ensure that our dynamically loaded `libclang`
617 // doesn't get dropped prematurely, nor is loaded multiple times
618 // across different threads.
619
620 lazy_static! {
621 static ref LIBCLANG: std::sync::Arc<clang_sys::SharedLibrary> = {
622 clang_sys::load().expect("Unable to find libclang");
623 clang_sys::get_library().expect(
624 "We just loaded libclang and it had better still be \
625 here!",
626 )
627 };
628 }
629
630 clang_sys::set_library(Some(LIBCLANG.clone()));
631}
632
633#[cfg(not(feature = "runtime"))]
634fn ensure_libclang_is_loaded() {}
635
636/// Error type for rust-bindgen.
637#[derive(Debug, Clone, PartialEq, Eq, Hash)]
638#[non_exhaustive]
639pub enum BindgenError {
640 /// The header was a folder.
641 FolderAsHeader(PathBuf),
642 /// Permissions to read the header is insufficient.
643 InsufficientPermissions(PathBuf),
644 /// The header does not exist.
645 NotExist(PathBuf),
646 /// Clang diagnosed an error.
647 ClangDiagnostic(String),
648 /// Code generation reported an error.
649 Codegen(CodegenError),
650}
651
652impl std::fmt::Display for BindgenError {
653 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
654 match self {
655 BindgenError::FolderAsHeader(h: &PathBuf) => {
656 write!(f, "'{}' is a folder", h.display())
657 }
658 BindgenError::InsufficientPermissions(h: &PathBuf) => {
659 write!(f, "insufficient permissions to read '{}'", h.display())
660 }
661 BindgenError::NotExist(h: &PathBuf) => {
662 write!(f, "header '{}' does not exist.", h.display())
663 }
664 BindgenError::ClangDiagnostic(message: &String) => {
665 write!(f, "clang diagnosed error: {}", message)
666 }
667 BindgenError::Codegen(err: &CodegenError) => {
668 write!(f, "codegen error: {}", err)
669 }
670 }
671 }
672}
673
674impl std::error::Error for BindgenError {}
675
676/// Generated Rust bindings.
677#[derive(Debug)]
678pub struct Bindings {
679 options: BindgenOptions,
680 module: proc_macro2::TokenStream,
681}
682
683pub(crate) const HOST_TARGET: &str =
684 include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
685
686// Some architecture triplets are different between rust and libclang, see #1211
687// and duplicates.
688fn rust_to_clang_target(rust_target: &str) -> Box<str> {
689 if rust_target.starts_with("aarch64-apple-") {
690 let mut clang_target = "arm64-apple-".to_owned();
691 clang_target
692 .push_str(rust_target.strip_prefix("aarch64-apple-").unwrap());
693 return clang_target.into();
694 } else if rust_target.starts_with("riscv64gc-") {
695 let mut clang_target = "riscv64-".to_owned();
696 clang_target.push_str(rust_target.strip_prefix("riscv64gc-").unwrap());
697 return clang_target.into();
698 } else if rust_target.ends_with("-espidf") {
699 let mut clang_target =
700 rust_target.strip_suffix("-espidf").unwrap().to_owned();
701 clang_target.push_str("-elf");
702 if clang_target.starts_with("riscv32imc-") {
703 clang_target = "riscv32-".to_owned() +
704 clang_target.strip_prefix("riscv32imc-").unwrap();
705 }
706 return clang_target.into();
707 } else if rust_target.starts_with("riscv32imc-") {
708 let mut clang_target = "riscv32-".to_owned();
709 clang_target.push_str(rust_target.strip_prefix("riscv32imc-").unwrap());
710 return clang_target.into();
711 } else if rust_target.starts_with("riscv32imac-") {
712 let mut clang_target = "riscv32-".to_owned();
713 clang_target
714 .push_str(rust_target.strip_prefix("riscv32imac-").unwrap());
715 return clang_target.into();
716 }
717 rust_target.into()
718}
719
720/// Returns the effective target, and whether it was explicitly specified on the
721/// clang flags.
722fn find_effective_target(clang_args: &[Box<str>]) -> (Box<str>, bool) {
723 let mut args: Iter<'_, Box> = clang_args.iter();
724 while let Some(opt: &Box) = args.next() {
725 if opt.starts_with("--target=") {
726 let mut split: Split<'_, char> = opt.split('=');
727 split.next();
728 return (split.next().unwrap().into(), true);
729 }
730
731 if opt.as_ref() == "-target" {
732 if let Some(target: &Box) = args.next() {
733 return (target.clone(), true);
734 }
735 }
736 }
737
738 // If we're running from a build script, try to find the cargo target.
739 if let Ok(t: String) = env::var(key:"TARGET") {
740 return (rust_to_clang_target(&t), false);
741 }
742
743 (rust_to_clang_target(HOST_TARGET), false)
744}
745
746impl Bindings {
747 /// Generate bindings for the given options.
748 pub(crate) fn generate(
749 mut options: BindgenOptions,
750 input_unsaved_files: Vec<clang::UnsavedFile>,
751 ) -> Result<Bindings, BindgenError> {
752 ensure_libclang_is_loaded();
753
754 #[cfg(feature = "runtime")]
755 debug!(
756 "Generating bindings, libclang at {}",
757 clang_sys::get_library().unwrap().path().display()
758 );
759 #[cfg(not(feature = "runtime"))]
760 debug!("Generating bindings, libclang linked");
761
762 options.build();
763
764 let (effective_target, explicit_target) =
765 find_effective_target(&options.clang_args);
766
767 let is_host_build =
768 rust_to_clang_target(HOST_TARGET) == effective_target;
769
770 // NOTE: The is_host_build check wouldn't be sound normally in some
771 // cases if we were to call a binary (if you have a 32-bit clang and are
772 // building on a 64-bit system for example). But since we rely on
773 // opening libclang.so, it has to be the same architecture and thus the
774 // check is fine.
775 if !explicit_target && !is_host_build {
776 options.clang_args.insert(
777 0,
778 format!("--target={}", effective_target).into_boxed_str(),
779 );
780 };
781
782 fn detect_include_paths(options: &mut BindgenOptions) {
783 if !options.detect_include_paths {
784 return;
785 }
786
787 // Filter out include paths and similar stuff, so we don't incorrectly
788 // promote them to `-isystem`.
789 let clang_args_for_clang_sys = {
790 let mut last_was_include_prefix = false;
791 options
792 .clang_args
793 .iter()
794 .filter(|arg| {
795 if last_was_include_prefix {
796 last_was_include_prefix = false;
797 return false;
798 }
799
800 let arg = arg.as_ref();
801
802 // https://clang.llvm.org/docs/ClangCommandLineReference.html
803 // -isystem and -isystem-after are harmless.
804 if arg == "-I" || arg == "--include-directory" {
805 last_was_include_prefix = true;
806 return false;
807 }
808
809 if arg.starts_with("-I") ||
810 arg.starts_with("--include-directory=")
811 {
812 return false;
813 }
814
815 true
816 })
817 .map(|arg| arg.clone().into())
818 .collect::<Vec<_>>()
819 };
820
821 debug!(
822 "Trying to find clang with flags: {:?}",
823 clang_args_for_clang_sys
824 );
825
826 let clang = match clang_sys::support::Clang::find(
827 None,
828 &clang_args_for_clang_sys,
829 ) {
830 None => return,
831 Some(clang) => clang,
832 };
833
834 debug!("Found clang: {:?}", clang);
835
836 // Whether we are working with C or C++ inputs.
837 let is_cpp = args_are_cpp(&options.clang_args) ||
838 options.input_headers.iter().any(|h| file_is_cpp(h));
839
840 let search_paths = if is_cpp {
841 clang.cpp_search_paths
842 } else {
843 clang.c_search_paths
844 };
845
846 if let Some(search_paths) = search_paths {
847 for path in search_paths.into_iter() {
848 if let Ok(path) = path.into_os_string().into_string() {
849 options.clang_args.push("-isystem".into());
850 options.clang_args.push(path.into_boxed_str());
851 }
852 }
853 }
854 }
855
856 detect_include_paths(&mut options);
857
858 #[cfg(unix)]
859 fn can_read(perms: &std::fs::Permissions) -> bool {
860 use std::os::unix::fs::PermissionsExt;
861 perms.mode() & 0o444 > 0
862 }
863
864 #[cfg(not(unix))]
865 fn can_read(_: &std::fs::Permissions) -> bool {
866 true
867 }
868
869 if let Some(h) = options.input_headers.last() {
870 let path = Path::new(h.as_ref());
871 if let Ok(md) = std::fs::metadata(path) {
872 if md.is_dir() {
873 return Err(BindgenError::FolderAsHeader(path.into()));
874 }
875 if !can_read(&md.permissions()) {
876 return Err(BindgenError::InsufficientPermissions(
877 path.into(),
878 ));
879 }
880 options.clang_args.push(h.clone());
881 } else {
882 return Err(BindgenError::NotExist(path.into()));
883 }
884 }
885
886 for (idx, f) in input_unsaved_files.iter().enumerate() {
887 if idx != 0 || !options.input_headers.is_empty() {
888 options.clang_args.push("-include".into());
889 }
890 options.clang_args.push(f.name.to_str().unwrap().into())
891 }
892
893 debug!("Fixed-up options: {:?}", options);
894
895 let time_phases = options.time_phases;
896 let mut context = BindgenContext::new(options, &input_unsaved_files);
897
898 if is_host_build {
899 debug_assert_eq!(
900 context.target_pointer_size(),
901 std::mem::size_of::<*mut ()>(),
902 "{:?} {:?}",
903 effective_target,
904 HOST_TARGET
905 );
906 }
907
908 {
909 let _t = time::Timer::new("parse").with_output(time_phases);
910 parse(&mut context)?;
911 }
912
913 let (module, options) =
914 codegen::codegen(context).map_err(BindgenError::Codegen)?;
915
916 Ok(Bindings { options, module })
917 }
918
919 /// Write these bindings as source text to a file.
920 pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
921 let file = OpenOptions::new()
922 .write(true)
923 .truncate(true)
924 .create(true)
925 .open(path.as_ref())?;
926 self.write(Box::new(file))?;
927 Ok(())
928 }
929
930 /// Write these bindings as source text to the given `Write`able.
931 pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> {
932 const NL: &str = if cfg!(windows) { "\r\n" } else { "\n" };
933
934 if !self.options.disable_header_comment {
935 let version =
936 option_env!("CARGO_PKG_VERSION").unwrap_or("(unknown version)");
937 writeln!(
938 writer,
939 "/* automatically generated by rust-bindgen {version} */{NL}",
940 )?;
941 }
942
943 for line in self.options.raw_lines.iter() {
944 writer.write_all(line.as_bytes())?;
945 writer.write_all(NL.as_bytes())?;
946 }
947
948 if !self.options.raw_lines.is_empty() {
949 writer.write_all(NL.as_bytes())?;
950 }
951
952 match self.format_tokens(&self.module) {
953 Ok(formatted_bindings) => {
954 writer.write_all(formatted_bindings.as_bytes())?;
955 }
956 Err(err) => {
957 eprintln!(
958 "Failed to run rustfmt: {} (non-fatal, continuing)",
959 err
960 );
961 writer.write_all(self.module.to_string().as_bytes())?;
962 }
963 }
964 Ok(())
965 }
966
967 /// Gets the rustfmt path to rustfmt the generated bindings.
968 fn rustfmt_path(&self) -> io::Result<Cow<PathBuf>> {
969 debug_assert!(matches!(self.options.formatter, Formatter::Rustfmt));
970 if let Some(ref p) = self.options.rustfmt_path {
971 return Ok(Cow::Borrowed(p));
972 }
973 if let Ok(rustfmt) = env::var("RUSTFMT") {
974 return Ok(Cow::Owned(rustfmt.into()));
975 }
976 #[cfg(feature = "which-rustfmt")]
977 match which::which("rustfmt") {
978 Ok(p) => Ok(Cow::Owned(p)),
979 Err(e) => {
980 Err(io::Error::new(io::ErrorKind::Other, format!("{}", e)))
981 }
982 }
983 #[cfg(not(feature = "which-rustfmt"))]
984 // No rustfmt binary was specified, so assume that the binary is called
985 // "rustfmt" and that it is in the user's PATH.
986 Ok(Cow::Owned("rustfmt".into()))
987 }
988
989 /// Formats a token stream with the formatter set up in `BindgenOptions`.
990 fn format_tokens(
991 &self,
992 tokens: &proc_macro2::TokenStream,
993 ) -> io::Result<String> {
994 let _t = time::Timer::new("rustfmt_generated_string")
995 .with_output(self.options.time_phases);
996
997 match self.options.formatter {
998 Formatter::None => return Ok(tokens.to_string()),
999 #[cfg(feature = "prettyplease")]
1000 Formatter::Prettyplease => {
1001 return Ok(prettyplease::unparse(&syn::parse_quote!(#tokens)));
1002 }
1003 Formatter::Rustfmt => (),
1004 }
1005
1006 let rustfmt = self.rustfmt_path()?;
1007 let mut cmd = Command::new(&*rustfmt);
1008
1009 cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
1010
1011 if let Some(path) = self
1012 .options
1013 .rustfmt_configuration_file
1014 .as_ref()
1015 .and_then(|f| f.to_str())
1016 {
1017 cmd.args(["--config-path", path]);
1018 }
1019
1020 let mut child = cmd.spawn()?;
1021 let mut child_stdin = child.stdin.take().unwrap();
1022 let mut child_stdout = child.stdout.take().unwrap();
1023
1024 let source = tokens.to_string();
1025
1026 // Write to stdin in a new thread, so that we can read from stdout on this
1027 // thread. This keeps the child from blocking on writing to its stdout which
1028 // might block us from writing to its stdin.
1029 let stdin_handle = ::std::thread::spawn(move || {
1030 let _ = child_stdin.write_all(source.as_bytes());
1031 source
1032 });
1033
1034 let mut output = vec![];
1035 io::copy(&mut child_stdout, &mut output)?;
1036
1037 let status = child.wait()?;
1038 let source = stdin_handle.join().expect(
1039 "The thread writing to rustfmt's stdin doesn't do \
1040 anything that could panic",
1041 );
1042
1043 match String::from_utf8(output) {
1044 Ok(bindings) => match status.code() {
1045 Some(0) => Ok(bindings),
1046 Some(2) => Err(io::Error::new(
1047 io::ErrorKind::Other,
1048 "Rustfmt parsing errors.".to_string(),
1049 )),
1050 Some(3) => {
1051 rustfmt_non_fatal_error_diagnostic(
1052 "Rustfmt could not format some lines",
1053 &self.options,
1054 );
1055 Ok(bindings)
1056 }
1057 _ => Err(io::Error::new(
1058 io::ErrorKind::Other,
1059 "Internal rustfmt error".to_string(),
1060 )),
1061 },
1062 _ => Ok(source),
1063 }
1064 }
1065}
1066
1067fn rustfmt_non_fatal_error_diagnostic(msg: &str, _options: &BindgenOptions) {
1068 warn!("{}", msg);
1069
1070 #[cfg(feature = "experimental")]
1071 if _options.emit_diagnostics {
1072 use crate::diagnostics::{Diagnostic, Level};
1073
1074 Diagnostic::default()
1075 .with_title(msg, Level::Warn)
1076 .add_annotation(
1077 "The bindings will be generated but not formatted.",
1078 Level::Note,
1079 )
1080 .display();
1081 }
1082}
1083
1084impl std::fmt::Display for Bindings {
1085 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1086 let mut bytes: Vec = vec![];
1087 self.write(Box::new(&mut bytes) as Box<dyn Write>)
1088 .expect(msg:"writing to a vec cannot fail");
1089 f.write_str(
1090 data:std::str::from_utf8(&bytes)
1091 .expect(msg:"we should only write bindings that are valid utf-8"),
1092 )
1093 }
1094}
1095
1096/// Determines whether the given cursor is in any of the files matched by the
1097/// options.
1098fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
1099 ctx.options().builtins || !cursor.is_builtin()
1100}
1101
1102/// Parse one `Item` from the Clang cursor.
1103fn parse_one(
1104 ctx: &mut BindgenContext,
1105 cursor: clang::Cursor,
1106 parent: Option<ItemId>,
1107) {
1108 if !filter_builtins(ctx, &cursor) {
1109 return;
1110 }
1111
1112 match Item::parse(cursor, parent_id:parent, ctx) {
1113 Ok(..) => {}
1114 Err(ParseError::Continue) => {}
1115 Err(ParseError::Recurse) => {
1116 cursor
1117 .visit_sorted(ctx, |ctx: &mut BindgenContext, child: Cursor| parse_one(ctx, cursor:child, parent));
1118 }
1119 }
1120}
1121
1122/// Parse the Clang AST into our `Item` internal representation.
1123fn parse(context: &mut BindgenContext) -> Result<(), BindgenError> {
1124 use clang_sys::*;
1125
1126 let mut error = None;
1127 for d in context.translation_unit().diags().iter() {
1128 let msg = d.format();
1129 let is_err = d.severity() >= CXDiagnostic_Error;
1130 if is_err {
1131 let error = error.get_or_insert_with(String::new);
1132 error.push_str(&msg);
1133 error.push('\n');
1134 } else {
1135 eprintln!("clang diag: {}", msg);
1136 }
1137 }
1138
1139 if let Some(message) = error {
1140 return Err(BindgenError::ClangDiagnostic(message));
1141 }
1142
1143 let cursor = context.translation_unit().cursor();
1144
1145 if context.options().emit_ast {
1146 fn dump_if_not_builtin(cur: &clang::Cursor) -> CXChildVisitResult {
1147 if !cur.is_builtin() {
1148 clang::ast_dump(cur, 0)
1149 } else {
1150 CXChildVisit_Continue
1151 }
1152 }
1153 cursor.visit(|cur| dump_if_not_builtin(&cur));
1154 }
1155
1156 let root = context.root_module();
1157 context.with_module(root, |ctx| {
1158 cursor.visit_sorted(ctx, |ctx, child| parse_one(ctx, child, None))
1159 });
1160
1161 assert!(
1162 context.current_module() == context.root_module(),
1163 "How did this happen?"
1164 );
1165 Ok(())
1166}
1167
1168/// Extracted Clang version data
1169#[derive(Debug)]
1170pub struct ClangVersion {
1171 /// Major and minor semver, if parsing was successful
1172 pub parsed: Option<(u32, u32)>,
1173 /// full version string
1174 pub full: String,
1175}
1176
1177/// Get the major and the minor semver numbers of Clang's version
1178pub fn clang_version() -> ClangVersion {
1179 ensure_libclang_is_loaded();
1180
1181 //Debian clang version 11.0.1-2
1182 let raw_v: String = clang::extract_clang_version();
1183 let split_v: Option<Vec<&str>> = raw_v
1184 .split_whitespace()
1185 .find(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit()))
1186 .map(|v| v.split('.').collect());
1187 if let Some(v) = split_v {
1188 if v.len() >= 2 {
1189 let maybe_major = v[0].parse::<u32>();
1190 let maybe_minor = v[1].parse::<u32>();
1191 if let (Ok(major), Ok(minor)) = (maybe_major, maybe_minor) {
1192 return ClangVersion {
1193 parsed: Some((major, minor)),
1194 full: raw_v.clone(),
1195 };
1196 }
1197 }
1198 };
1199 ClangVersion {
1200 parsed: None,
1201 full: raw_v.clone(),
1202 }
1203}
1204
1205fn env_var<K: AsRef<str> + AsRef<OsStr>>(
1206 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1207 key: K,
1208) -> Result<String, std::env::VarError> {
1209 for callback: &Rc in parse_callbacks {
1210 callback.read_env_var(key.as_ref());
1211 }
1212 std::env::var(key)
1213}
1214
1215/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found.
1216fn get_target_dependent_env_var(
1217 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1218 var: &str,
1219) -> Option<String> {
1220 if let Ok(target: String) = env_var(parse_callbacks, key:"TARGET") {
1221 if let Ok(v: String) = env_var(parse_callbacks, key:format!("{}_{}", var, target)) {
1222 return Some(v);
1223 }
1224 if let Ok(v: String) = env_var(
1225 parse_callbacks,
1226 key:format!("{}_{}", var, target.replace('-', "_")),
1227 ) {
1228 return Some(v);
1229 }
1230 }
1231
1232 env_var(parse_callbacks, key:var).ok()
1233}
1234
1235/// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed
1236/// line and on env variable usage by echoing a rerun-if-env-changed line
1237///
1238/// When running inside a `build.rs` script, this can be used to make cargo invalidate the
1239/// generated bindings whenever any of the files included from the header change:
1240/// ```
1241/// use bindgen::builder;
1242/// let bindings = builder()
1243/// .header("path/to/input/header")
1244/// .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
1245/// .generate();
1246/// ```
1247#[derive(Debug)]
1248pub struct CargoCallbacks {
1249 rerun_on_header_files: bool,
1250}
1251
1252/// Create a new `CargoCallbacks` value with [`CargoCallbacks::rerun_on_header_files`] disabled.
1253///
1254/// This constructor has been deprecated in favor of [`CargoCallbacks::new`] where
1255/// [`CargoCallbacks::rerun_on_header_files`] is enabled by default.
1256#[deprecated = "Use `CargoCallbacks::new()` instead. Please, check the documentation for further information."]
1257pub const CargoCallbacks: CargoCallbacks = CargoCallbacks {
1258 rerun_on_header_files: false,
1259};
1260
1261impl CargoCallbacks {
1262 /// Create a new `CargoCallbacks` value.
1263 pub fn new() -> Self {
1264 Self {
1265 rerun_on_header_files: true,
1266 }
1267 }
1268
1269 /// Whether Cargo should re-run the build script if any of the input header files has changed.
1270 ///
1271 /// This option is enabled by default unless the deprecated [`const@CargoCallbacks`]
1272 /// constructor is used.
1273 pub fn rerun_on_header_files(mut self, doit: bool) -> Self {
1274 self.rerun_on_header_files = doit;
1275 self
1276 }
1277}
1278
1279impl Default for CargoCallbacks {
1280 fn default() -> Self {
1281 Self::new()
1282 }
1283}
1284
1285impl callbacks::ParseCallbacks for CargoCallbacks {
1286 fn header_file(&self, filename: &str) {
1287 if self.rerun_on_header_files {
1288 println!("cargo:rerun-if-changed={}", filename);
1289 }
1290 }
1291
1292 fn include_file(&self, filename: &str) {
1293 println!("cargo:rerun-if-changed={}", filename);
1294 }
1295
1296 fn read_env_var(&self, key: &str) {
1297 println!("cargo:rerun-if-env-changed={}", key);
1298 }
1299}
1300
1301/// Test command_line_flag function.
1302#[test]
1303fn commandline_flag_unit_test_function() {
1304 //Test 1
1305 let bindings = crate::builder();
1306 let command_line_flags = bindings.command_line_flags();
1307
1308 let test_cases = [
1309 "--rust-target",
1310 "--no-derive-default",
1311 "--generate",
1312 "functions,types,vars,methods,constructors,destructors",
1313 ]
1314 .iter()
1315 .map(|&x| x.into())
1316 .collect::<Vec<String>>();
1317
1318 assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
1319
1320 //Test 2
1321 let bindings = crate::builder()
1322 .header("input_header")
1323 .allowlist_type("Distinct_Type")
1324 .allowlist_function("safe_function");
1325
1326 let command_line_flags = bindings.command_line_flags();
1327 let test_cases = [
1328 "--rust-target",
1329 "input_header",
1330 "--no-derive-default",
1331 "--generate",
1332 "functions,types,vars,methods,constructors,destructors",
1333 "--allowlist-type",
1334 "Distinct_Type",
1335 "--allowlist-function",
1336 "safe_function",
1337 ]
1338 .iter()
1339 .map(|&x| x.into())
1340 .collect::<Vec<String>>();
1341 println!("{:?}", command_line_flags);
1342
1343 assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
1344}
1345
1346#[test]
1347fn test_rust_to_clang_target() {
1348 assert_eq!(
1349 rust_to_clang_target("aarch64-apple-ios").as_ref(),
1350 "arm64-apple-ios"
1351 );
1352}
1353
1354#[test]
1355fn test_rust_to_clang_target_riscv() {
1356 assert_eq!(
1357 rust_to_clang_target("riscv64gc-unknown-linux-gnu").as_ref(),
1358 "riscv64-unknown-linux-gnu"
1359 );
1360 assert_eq!(
1361 rust_to_clang_target("riscv32imc-unknown-none-elf").as_ref(),
1362 "riscv32-unknown-none-elf"
1363 );
1364 assert_eq!(
1365 rust_to_clang_target("riscv32imac-unknown-none-elf").as_ref(),
1366 "riscv32-unknown-none-elf"
1367 );
1368}
1369
1370#[test]
1371fn test_rust_to_clang_target_espidf() {
1372 assert_eq!(
1373 rust_to_clang_target("riscv32imc-esp-espidf").as_ref(),
1374 "riscv32-esp-elf"
1375 );
1376 assert_eq!(
1377 rust_to_clang_target("xtensa-esp32-espidf").as_ref(),
1378 "xtensa-esp32-elf"
1379 );
1380}
1381