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 quote;
23
24#[cfg(feature = "logging")]
25#[macro_use]
26extern crate log;
27
28#[cfg(not(feature = "logging"))]
29#[macro_use]
30mod log_stubs;
31
32#[macro_use]
33mod extra_assertions;
34
35mod codegen;
36mod deps;
37mod options;
38mod time;
39
40pub mod callbacks;
41
42mod clang;
43#[cfg(feature = "experimental")]
44mod diagnostics;
45mod features;
46mod ir;
47mod parse;
48mod regex_set;
49
50pub use codegen::{
51 AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
52};
53#[cfg(feature = "__cli")]
54pub use features::RUST_TARGET_STRINGS;
55pub use features::{RustTarget, LATEST_STABLE_RUST};
56pub use ir::annotations::FieldVisibilityKind;
57pub use ir::function::Abi;
58pub use regex_set::RegexSet;
59
60use codegen::CodegenError;
61use features::RustFeatures;
62use ir::comment;
63use ir::context::{BindgenContext, ItemId};
64use ir::item::Item;
65use options::BindgenOptions;
66use parse::ParseError;
67
68use std::borrow::Cow;
69use std::collections::hash_map::Entry;
70use std::env;
71use std::ffi::OsStr;
72use std::fs::{File, OpenOptions};
73use std::io::{self, Write};
74use std::path::{Path, PathBuf};
75use std::process::{Command, Stdio};
76use std::rc::Rc;
77use std::str::FromStr;
78use std::sync::{Arc, OnceLock};
79
80// Some convenient typedefs for a fast hash map and hash set.
81type HashMap<K, V> = rustc_hash::FxHashMap<K, V>;
82type HashSet<K> = rustc_hash::FxHashSet<K>;
83
84/// Default prefix for the anon fields.
85pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
86
87const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern";
88
89fn file_is_cpp(name_file: &str) -> bool {
90 name_file.ends_with(".hpp") ||
91 name_file.ends_with(".hxx") ||
92 name_file.ends_with(".hh") ||
93 name_file.ends_with(".h++")
94}
95
96fn args_are_cpp(clang_args: &[Box<str>]) -> bool {
97 for w: &[Box] in clang_args.windows(size:2) {
98 if w[0].as_ref() == "-xc++" || w[1].as_ref() == "-xc++" {
99 return true;
100 }
101 if w[0].as_ref() == "-x" && w[1].as_ref() == "c++" {
102 return true;
103 }
104 if w[0].as_ref() == "-include" && file_is_cpp(name_file:w[1].as_ref()) {
105 return true;
106 }
107 }
108 false
109}
110
111bitflags! {
112 /// A type used to indicate which kind of items we have to generate.
113 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
114 pub struct CodegenConfig: u32 {
115 /// Whether to generate functions.
116 const FUNCTIONS = 1 << 0;
117 /// Whether to generate types.
118 const TYPES = 1 << 1;
119 /// Whether to generate constants.
120 const VARS = 1 << 2;
121 /// Whether to generate methods.
122 const METHODS = 1 << 3;
123 /// Whether to generate constructors
124 const CONSTRUCTORS = 1 << 4;
125 /// Whether to generate destructors.
126 const DESTRUCTORS = 1 << 5;
127 }
128}
129
130impl CodegenConfig {
131 /// Returns true if functions should be generated.
132 pub fn functions(self) -> bool {
133 self.contains(CodegenConfig::FUNCTIONS)
134 }
135
136 /// Returns true if types should be generated.
137 pub fn types(self) -> bool {
138 self.contains(CodegenConfig::TYPES)
139 }
140
141 /// Returns true if constants should be generated.
142 pub fn vars(self) -> bool {
143 self.contains(CodegenConfig::VARS)
144 }
145
146 /// Returns true if methods should be generated.
147 pub fn methods(self) -> bool {
148 self.contains(CodegenConfig::METHODS)
149 }
150
151 /// Returns true if constructors should be generated.
152 pub fn constructors(self) -> bool {
153 self.contains(CodegenConfig::CONSTRUCTORS)
154 }
155
156 /// Returns true if destructors should be generated.
157 pub fn destructors(self) -> bool {
158 self.contains(CodegenConfig::DESTRUCTORS)
159 }
160}
161
162impl Default for CodegenConfig {
163 fn default() -> Self {
164 CodegenConfig::all()
165 }
166}
167
168/// Formatting tools that can be used to format the bindings
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170#[non_exhaustive]
171pub enum Formatter {
172 /// Do not format the bindings.
173 None,
174 /// Use `rustfmt` to format the bindings.
175 Rustfmt,
176 #[cfg(feature = "prettyplease")]
177 /// Use `prettyplease` to format the bindings.
178 Prettyplease,
179}
180
181impl Default for Formatter {
182 fn default() -> Self {
183 Self::Rustfmt
184 }
185}
186
187impl FromStr for Formatter {
188 type Err = String;
189
190 fn from_str(s: &str) -> Result<Self, Self::Err> {
191 match s {
192 "none" => Ok(Self::None),
193 "rustfmt" => Ok(Self::Rustfmt),
194 #[cfg(feature = "prettyplease")]
195 "prettyplease" => Ok(Self::Prettyplease),
196 _ => Err(format!("`{}` is not a valid formatter", s)),
197 }
198 }
199}
200
201impl std::fmt::Display for Formatter {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 let s: &'static str = match self {
204 Self::None => "none",
205 Self::Rustfmt => "rustfmt",
206 #[cfg(feature = "prettyplease")]
207 Self::Prettyplease => "prettyplease",
208 };
209
210 s.fmt(f)
211 }
212}
213
214/// Configure and generate Rust bindings for a C/C++ header.
215///
216/// This is the main entry point to the library.
217///
218/// ```ignore
219/// use bindgen::builder;
220///
221/// // Configure and generate bindings.
222/// let bindings = builder().header("path/to/input/header")
223/// .allowlist_type("SomeCoolClass")
224/// .allowlist_function("do_some_cool_thing")
225/// .generate()?;
226///
227/// // Write the generated bindings to an output file.
228/// bindings.write_to_file("path/to/output.rs")?;
229/// ```
230///
231/// # Enums
232///
233/// Bindgen can map C/C++ enums into Rust in different ways. The way bindgen maps enums depends on
234/// the pattern passed to several methods:
235///
236/// 1. [`constified_enum_module()`](#method.constified_enum_module)
237/// 2. [`bitfield_enum()`](#method.bitfield_enum)
238/// 3. [`newtype_enum()`](#method.newtype_enum)
239/// 4. [`rustified_enum()`](#method.rustified_enum)
240/// 5. [`rustified_non_exhaustive_enum()`](#method.rustified_non_exhaustive_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 static LIBCLANG: OnceLock<Arc<clang_sys::SharedLibrary>> = OnceLock::new();
621 let libclang: &Arc = LIBCLANG.get_or_init(|| {
622 clang_sys::load().expect(msg:"Unable to find libclang");
623 clang_sys::get_library()
624 .expect(msg:"We just loaded libclang and it had better still be here!")
625 });
626
627 clang_sys::set_library(Some(libclang.clone()));
628}
629
630#[cfg(not(feature = "runtime"))]
631fn ensure_libclang_is_loaded() {}
632
633/// Error type for rust-bindgen.
634#[derive(Debug, Clone, PartialEq, Eq, Hash)]
635#[non_exhaustive]
636pub enum BindgenError {
637 /// The header was a folder.
638 FolderAsHeader(PathBuf),
639 /// Permissions to read the header is insufficient.
640 InsufficientPermissions(PathBuf),
641 /// The header does not exist.
642 NotExist(PathBuf),
643 /// Clang diagnosed an error.
644 ClangDiagnostic(String),
645 /// Code generation reported an error.
646 Codegen(CodegenError),
647}
648
649impl std::fmt::Display for BindgenError {
650 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
651 match self {
652 BindgenError::FolderAsHeader(h: &PathBuf) => {
653 write!(f, "'{}' is a folder", h.display())
654 }
655 BindgenError::InsufficientPermissions(h: &PathBuf) => {
656 write!(f, "insufficient permissions to read '{}'", h.display())
657 }
658 BindgenError::NotExist(h: &PathBuf) => {
659 write!(f, "header '{}' does not exist.", h.display())
660 }
661 BindgenError::ClangDiagnostic(message: &String) => {
662 write!(f, "clang diagnosed error: {}", message)
663 }
664 BindgenError::Codegen(err: &CodegenError) => {
665 write!(f, "codegen error: {}", err)
666 }
667 }
668 }
669}
670
671impl std::error::Error for BindgenError {}
672
673/// Generated Rust bindings.
674#[derive(Debug)]
675pub struct Bindings {
676 options: BindgenOptions,
677 module: proc_macro2::TokenStream,
678}
679
680pub(crate) const HOST_TARGET: &str =
681 include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
682
683// Some architecture triplets are different between rust and libclang, see #1211
684// and duplicates.
685fn rust_to_clang_target(rust_target: &str) -> Box<str> {
686 const TRIPLE_HYPHENS_MESSAGE: &str = "Target triple should contain hyphens";
687
688 let mut clang_target = rust_target.to_owned();
689
690 if clang_target.starts_with("riscv32") {
691 let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
692
693 clang_target.replace_range(..idx, "riscv32");
694 } else if clang_target.starts_with("riscv64") {
695 let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
696
697 clang_target.replace_range(..idx, "riscv64");
698 } else if clang_target.starts_with("aarch64-apple-") {
699 let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
700
701 clang_target.replace_range(..idx, "arm64");
702 }
703
704 if clang_target.ends_with("-espidf") {
705 let idx = clang_target.rfind('-').expect(TRIPLE_HYPHENS_MESSAGE);
706
707 clang_target.replace_range((idx + 1).., "elf");
708 }
709
710 clang_target.into()
711}
712
713/// Returns the effective target, and whether it was explicitly specified on the
714/// clang flags.
715fn find_effective_target(clang_args: &[Box<str>]) -> (Box<str>, bool) {
716 let mut args: Iter<'_, Box> = clang_args.iter();
717 while let Some(opt: &Box) = args.next() {
718 if opt.starts_with("--target=") {
719 let mut split: Split<'_, char> = opt.split('=');
720 split.next();
721 return (split.next().unwrap().into(), true);
722 }
723
724 if opt.as_ref() == "-target" {
725 if let Some(target: &Box) = args.next() {
726 return (target.clone(), true);
727 }
728 }
729 }
730
731 // If we're running from a build script, try to find the cargo target.
732 if let Ok(t: String) = env::var(key:"TARGET") {
733 return (rust_to_clang_target(&t), false);
734 }
735
736 (rust_to_clang_target(HOST_TARGET), false)
737}
738
739impl Bindings {
740 /// Generate bindings for the given options.
741 pub(crate) fn generate(
742 mut options: BindgenOptions,
743 input_unsaved_files: Vec<clang::UnsavedFile>,
744 ) -> Result<Bindings, BindgenError> {
745 ensure_libclang_is_loaded();
746
747 #[cfg(feature = "runtime")]
748 debug!(
749 "Generating bindings, libclang at {}",
750 clang_sys::get_library().unwrap().path().display()
751 );
752 #[cfg(not(feature = "runtime"))]
753 debug!("Generating bindings, libclang linked");
754
755 options.build();
756
757 let (effective_target, explicit_target) =
758 find_effective_target(&options.clang_args);
759
760 let is_host_build =
761 rust_to_clang_target(HOST_TARGET) == effective_target;
762
763 // NOTE: The is_host_build check wouldn't be sound normally in some
764 // cases if we were to call a binary (if you have a 32-bit clang and are
765 // building on a 64-bit system for example). But since we rely on
766 // opening libclang.so, it has to be the same architecture and thus the
767 // check is fine.
768 if !explicit_target && !is_host_build {
769 options.clang_args.insert(
770 0,
771 format!("--target={}", effective_target).into_boxed_str(),
772 );
773 };
774
775 fn detect_include_paths(options: &mut BindgenOptions) {
776 if !options.detect_include_paths {
777 return;
778 }
779
780 // Filter out include paths and similar stuff, so we don't incorrectly
781 // promote them to `-isystem`.
782 let clang_args_for_clang_sys = {
783 let mut last_was_include_prefix = false;
784 options
785 .clang_args
786 .iter()
787 .filter(|arg| {
788 if last_was_include_prefix {
789 last_was_include_prefix = false;
790 return false;
791 }
792
793 let arg = arg.as_ref();
794
795 // https://clang.llvm.org/docs/ClangCommandLineReference.html
796 // -isystem and -isystem-after are harmless.
797 if arg == "-I" || arg == "--include-directory" {
798 last_was_include_prefix = true;
799 return false;
800 }
801
802 if arg.starts_with("-I") ||
803 arg.starts_with("--include-directory=")
804 {
805 return false;
806 }
807
808 true
809 })
810 .map(|arg| arg.clone().into())
811 .collect::<Vec<_>>()
812 };
813
814 debug!(
815 "Trying to find clang with flags: {:?}",
816 clang_args_for_clang_sys
817 );
818
819 let clang = match clang_sys::support::Clang::find(
820 None,
821 &clang_args_for_clang_sys,
822 ) {
823 None => return,
824 Some(clang) => clang,
825 };
826
827 debug!("Found clang: {:?}", clang);
828
829 // Whether we are working with C or C++ inputs.
830 let is_cpp = args_are_cpp(&options.clang_args) ||
831 options.input_headers.iter().any(|h| file_is_cpp(h));
832
833 let search_paths = if is_cpp {
834 clang.cpp_search_paths
835 } else {
836 clang.c_search_paths
837 };
838
839 if let Some(search_paths) = search_paths {
840 for path in search_paths.into_iter() {
841 if let Ok(path) = path.into_os_string().into_string() {
842 options.clang_args.push("-isystem".into());
843 options.clang_args.push(path.into_boxed_str());
844 }
845 }
846 }
847 }
848
849 detect_include_paths(&mut options);
850
851 #[cfg(unix)]
852 fn can_read(perms: &std::fs::Permissions) -> bool {
853 use std::os::unix::fs::PermissionsExt;
854 perms.mode() & 0o444 > 0
855 }
856
857 #[cfg(not(unix))]
858 fn can_read(_: &std::fs::Permissions) -> bool {
859 true
860 }
861
862 if let Some(h) = options.input_headers.last() {
863 let path = Path::new(h.as_ref());
864 if let Ok(md) = std::fs::metadata(path) {
865 if md.is_dir() {
866 return Err(BindgenError::FolderAsHeader(path.into()));
867 }
868 if !can_read(&md.permissions()) {
869 return Err(BindgenError::InsufficientPermissions(
870 path.into(),
871 ));
872 }
873 options.clang_args.push(h.clone());
874 } else {
875 return Err(BindgenError::NotExist(path.into()));
876 }
877 }
878
879 for (idx, f) in input_unsaved_files.iter().enumerate() {
880 if idx != 0 || !options.input_headers.is_empty() {
881 options.clang_args.push("-include".into());
882 }
883 options.clang_args.push(f.name.to_str().unwrap().into())
884 }
885
886 debug!("Fixed-up options: {:?}", options);
887
888 let time_phases = options.time_phases;
889 let mut context = BindgenContext::new(options, &input_unsaved_files);
890
891 if is_host_build {
892 debug_assert_eq!(
893 context.target_pointer_size(),
894 std::mem::size_of::<*mut ()>(),
895 "{:?} {:?}",
896 effective_target,
897 HOST_TARGET
898 );
899 }
900
901 {
902 let _t = time::Timer::new("parse").with_output(time_phases);
903 parse(&mut context)?;
904 }
905
906 let (module, options) =
907 codegen::codegen(context).map_err(BindgenError::Codegen)?;
908
909 Ok(Bindings { options, module })
910 }
911
912 /// Write these bindings as source text to a file.
913 pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
914 let file = OpenOptions::new()
915 .write(true)
916 .truncate(true)
917 .create(true)
918 .open(path.as_ref())?;
919 self.write(Box::new(file))?;
920 Ok(())
921 }
922
923 /// Write these bindings as source text to the given `Write`able.
924 pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> {
925 const NL: &str = if cfg!(windows) { "\r\n" } else { "\n" };
926
927 if !self.options.disable_header_comment {
928 let version =
929 option_env!("CARGO_PKG_VERSION").unwrap_or("(unknown version)");
930 writeln!(
931 writer,
932 "/* automatically generated by rust-bindgen {version} */{NL}",
933 )?;
934 }
935
936 for line in self.options.raw_lines.iter() {
937 writer.write_all(line.as_bytes())?;
938 writer.write_all(NL.as_bytes())?;
939 }
940
941 if !self.options.raw_lines.is_empty() {
942 writer.write_all(NL.as_bytes())?;
943 }
944
945 match self.format_tokens(&self.module) {
946 Ok(formatted_bindings) => {
947 writer.write_all(formatted_bindings.as_bytes())?;
948 }
949 Err(err) => {
950 eprintln!(
951 "Failed to run rustfmt: {} (non-fatal, continuing)",
952 err
953 );
954 writer.write_all(self.module.to_string().as_bytes())?;
955 }
956 }
957 Ok(())
958 }
959
960 /// Gets the rustfmt path to rustfmt the generated bindings.
961 fn rustfmt_path(&self) -> io::Result<Cow<PathBuf>> {
962 debug_assert!(matches!(self.options.formatter, Formatter::Rustfmt));
963 if let Some(ref p) = self.options.rustfmt_path {
964 return Ok(Cow::Borrowed(p));
965 }
966 if let Ok(rustfmt) = env::var("RUSTFMT") {
967 return Ok(Cow::Owned(rustfmt.into()));
968 }
969 // No rustfmt binary was specified, so assume that the binary is called
970 // "rustfmt" and that it is in the user's PATH.
971 Ok(Cow::Owned("rustfmt".into()))
972 }
973
974 /// Formats a token stream with the formatter set up in `BindgenOptions`.
975 fn format_tokens(
976 &self,
977 tokens: &proc_macro2::TokenStream,
978 ) -> io::Result<String> {
979 let _t = time::Timer::new("rustfmt_generated_string")
980 .with_output(self.options.time_phases);
981
982 match self.options.formatter {
983 Formatter::None => return Ok(tokens.to_string()),
984 #[cfg(feature = "prettyplease")]
985 Formatter::Prettyplease => {
986 return Ok(prettyplease::unparse(&syn::parse_quote!(#tokens)));
987 }
988 Formatter::Rustfmt => (),
989 }
990
991 let rustfmt = self.rustfmt_path()?;
992 let mut cmd = Command::new(&*rustfmt);
993
994 cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
995
996 if let Some(path) = self
997 .options
998 .rustfmt_configuration_file
999 .as_ref()
1000 .and_then(|f| f.to_str())
1001 {
1002 cmd.args(["--config-path", path]);
1003 }
1004
1005 let mut child = cmd.spawn()?;
1006 let mut child_stdin = child.stdin.take().unwrap();
1007 let mut child_stdout = child.stdout.take().unwrap();
1008
1009 let source = tokens.to_string();
1010
1011 // Write to stdin in a new thread, so that we can read from stdout on this
1012 // thread. This keeps the child from blocking on writing to its stdout which
1013 // might block us from writing to its stdin.
1014 let stdin_handle = ::std::thread::spawn(move || {
1015 let _ = child_stdin.write_all(source.as_bytes());
1016 source
1017 });
1018
1019 let mut output = vec![];
1020 io::copy(&mut child_stdout, &mut output)?;
1021
1022 let status = child.wait()?;
1023 let source = stdin_handle.join().expect(
1024 "The thread writing to rustfmt's stdin doesn't do \
1025 anything that could panic",
1026 );
1027
1028 match String::from_utf8(output) {
1029 Ok(bindings) => match status.code() {
1030 Some(0) => Ok(bindings),
1031 Some(2) => Err(io::Error::new(
1032 io::ErrorKind::Other,
1033 "Rustfmt parsing errors.".to_string(),
1034 )),
1035 Some(3) => {
1036 rustfmt_non_fatal_error_diagnostic(
1037 "Rustfmt could not format some lines",
1038 &self.options,
1039 );
1040 Ok(bindings)
1041 }
1042 _ => Err(io::Error::new(
1043 io::ErrorKind::Other,
1044 "Internal rustfmt error".to_string(),
1045 )),
1046 },
1047 _ => Ok(source),
1048 }
1049 }
1050}
1051
1052fn rustfmt_non_fatal_error_diagnostic(msg: &str, _options: &BindgenOptions) {
1053 warn!("{}", msg);
1054
1055 #[cfg(feature = "experimental")]
1056 if _options.emit_diagnostics {
1057 use crate::diagnostics::{Diagnostic, Level};
1058
1059 Diagnostic::default()
1060 .with_title(msg, Level::Warn)
1061 .add_annotation(
1062 "The bindings will be generated but not formatted.",
1063 Level::Note,
1064 )
1065 .display();
1066 }
1067}
1068
1069impl std::fmt::Display for Bindings {
1070 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1071 let mut bytes: Vec = vec![];
1072 self.write(Box::new(&mut bytes) as Box<dyn Write>)
1073 .expect(msg:"writing to a vec cannot fail");
1074 f.write_str(
1075 data:std::str::from_utf8(&bytes)
1076 .expect(msg:"we should only write bindings that are valid utf-8"),
1077 )
1078 }
1079}
1080
1081/// Determines whether the given cursor is in any of the files matched by the
1082/// options.
1083fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
1084 ctx.options().builtins || !cursor.is_builtin()
1085}
1086
1087/// Parse one `Item` from the Clang cursor.
1088fn parse_one(
1089 ctx: &mut BindgenContext,
1090 cursor: clang::Cursor,
1091 parent: Option<ItemId>,
1092) {
1093 if !filter_builtins(ctx, &cursor) {
1094 return;
1095 }
1096
1097 match Item::parse(cursor, parent, ctx) {
1098 Ok(..) => {}
1099 Err(ParseError::Continue) => {}
1100 Err(ParseError::Recurse) => {
1101 cursor
1102 .visit_sorted(ctx, |ctx: &mut BindgenContext, child: Cursor| parse_one(ctx, cursor:child, parent));
1103 }
1104 }
1105}
1106
1107/// Parse the Clang AST into our `Item` internal representation.
1108fn parse(context: &mut BindgenContext) -> Result<(), BindgenError> {
1109 use clang_sys::*;
1110
1111 let mut error = None;
1112 for d in context.translation_unit().diags().iter() {
1113 let msg = d.format();
1114 let is_err = d.severity() >= CXDiagnostic_Error;
1115 if is_err {
1116 let error = error.get_or_insert_with(String::new);
1117 error.push_str(&msg);
1118 error.push('\n');
1119 } else {
1120 eprintln!("clang diag: {}", msg);
1121 }
1122 }
1123
1124 if let Some(message) = error {
1125 return Err(BindgenError::ClangDiagnostic(message));
1126 }
1127
1128 let cursor = context.translation_unit().cursor();
1129
1130 if context.options().emit_ast {
1131 fn dump_if_not_builtin(cur: &clang::Cursor) -> CXChildVisitResult {
1132 if !cur.is_builtin() {
1133 clang::ast_dump(cur, 0)
1134 } else {
1135 CXChildVisit_Continue
1136 }
1137 }
1138 cursor.visit(|cur| dump_if_not_builtin(&cur));
1139 }
1140
1141 let root = context.root_module();
1142 context.with_module(root, |ctx| {
1143 cursor.visit_sorted(ctx, |ctx, child| parse_one(ctx, child, None))
1144 });
1145
1146 assert!(
1147 context.current_module() == context.root_module(),
1148 "How did this happen?"
1149 );
1150 Ok(())
1151}
1152
1153/// Extracted Clang version data
1154#[derive(Debug)]
1155pub struct ClangVersion {
1156 /// Major and minor semver, if parsing was successful
1157 pub parsed: Option<(u32, u32)>,
1158 /// full version string
1159 pub full: String,
1160}
1161
1162/// Get the major and the minor semver numbers of Clang's version
1163pub fn clang_version() -> ClangVersion {
1164 ensure_libclang_is_loaded();
1165
1166 //Debian clang version 11.0.1-2
1167 let raw_v: String = clang::extract_clang_version();
1168 let split_v: Option<Vec<&str>> = raw_v
1169 .split_whitespace()
1170 .find(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit()))
1171 .map(|v| v.split('.').collect());
1172 if let Some(v) = split_v {
1173 if v.len() >= 2 {
1174 let maybe_major = v[0].parse::<u32>();
1175 let maybe_minor = v[1].parse::<u32>();
1176 if let (Ok(major), Ok(minor)) = (maybe_major, maybe_minor) {
1177 return ClangVersion {
1178 parsed: Some((major, minor)),
1179 full: raw_v.clone(),
1180 };
1181 }
1182 }
1183 };
1184 ClangVersion {
1185 parsed: None,
1186 full: raw_v.clone(),
1187 }
1188}
1189
1190fn env_var<K: AsRef<str> + AsRef<OsStr>>(
1191 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1192 key: K,
1193) -> Result<String, std::env::VarError> {
1194 for callback: &Rc in parse_callbacks {
1195 callback.read_env_var(key.as_ref());
1196 }
1197 std::env::var(key)
1198}
1199
1200/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found.
1201fn get_target_dependent_env_var(
1202 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1203 var: &str,
1204) -> Option<String> {
1205 if let Ok(target: String) = env_var(parse_callbacks, key:"TARGET") {
1206 if let Ok(v: String) = env_var(parse_callbacks, key:format!("{}_{}", var, target)) {
1207 return Some(v);
1208 }
1209 if let Ok(v: String) = env_var(
1210 parse_callbacks,
1211 key:format!("{}_{}", var, target.replace('-', "_")),
1212 ) {
1213 return Some(v);
1214 }
1215 }
1216
1217 env_var(parse_callbacks, key:var).ok()
1218}
1219
1220/// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed
1221/// line and on env variable usage by echoing a rerun-if-env-changed line
1222///
1223/// When running inside a `build.rs` script, this can be used to make cargo invalidate the
1224/// generated bindings whenever any of the files included from the header change:
1225/// ```
1226/// use bindgen::builder;
1227/// let bindings = builder()
1228/// .header("path/to/input/header")
1229/// .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
1230/// .generate();
1231/// ```
1232#[derive(Debug)]
1233pub struct CargoCallbacks {
1234 rerun_on_header_files: bool,
1235}
1236
1237/// Create a new `CargoCallbacks` value with [`CargoCallbacks::rerun_on_header_files`] disabled.
1238///
1239/// This constructor has been deprecated in favor of [`CargoCallbacks::new`] where
1240/// [`CargoCallbacks::rerun_on_header_files`] is enabled by default.
1241#[deprecated = "Use `CargoCallbacks::new()` instead. Please, check the documentation for further information."]
1242pub const CargoCallbacks: CargoCallbacks = CargoCallbacks {
1243 rerun_on_header_files: false,
1244};
1245
1246impl CargoCallbacks {
1247 /// Create a new `CargoCallbacks` value.
1248 pub fn new() -> Self {
1249 Self {
1250 rerun_on_header_files: true,
1251 }
1252 }
1253
1254 /// Whether Cargo should re-run the build script if any of the input header files has changed.
1255 ///
1256 /// This option is enabled by default unless the deprecated [`const@CargoCallbacks`]
1257 /// constructor is used.
1258 pub fn rerun_on_header_files(mut self, doit: bool) -> Self {
1259 self.rerun_on_header_files = doit;
1260 self
1261 }
1262}
1263
1264impl Default for CargoCallbacks {
1265 fn default() -> Self {
1266 Self::new()
1267 }
1268}
1269
1270impl callbacks::ParseCallbacks for CargoCallbacks {
1271 fn header_file(&self, filename: &str) {
1272 if self.rerun_on_header_files {
1273 println!("cargo:rerun-if-changed={}", filename);
1274 }
1275 }
1276
1277 fn include_file(&self, filename: &str) {
1278 println!("cargo:rerun-if-changed={}", filename);
1279 }
1280
1281 fn read_env_var(&self, key: &str) {
1282 println!("cargo:rerun-if-env-changed={}", key);
1283 }
1284}
1285
1286/// Test command_line_flag function.
1287#[test]
1288fn commandline_flag_unit_test_function() {
1289 //Test 1
1290 let bindings = crate::builder();
1291 let command_line_flags = bindings.command_line_flags();
1292
1293 let test_cases = [
1294 "--rust-target",
1295 "--no-derive-default",
1296 "--generate",
1297 "functions,types,vars,methods,constructors,destructors",
1298 ]
1299 .iter()
1300 .map(|&x| x.into())
1301 .collect::<Vec<String>>();
1302
1303 assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
1304
1305 //Test 2
1306 let bindings = crate::builder()
1307 .header("input_header")
1308 .allowlist_type("Distinct_Type")
1309 .allowlist_function("safe_function");
1310
1311 let command_line_flags = bindings.command_line_flags();
1312 let test_cases = [
1313 "--rust-target",
1314 "input_header",
1315 "--no-derive-default",
1316 "--generate",
1317 "functions,types,vars,methods,constructors,destructors",
1318 "--allowlist-type",
1319 "Distinct_Type",
1320 "--allowlist-function",
1321 "safe_function",
1322 ]
1323 .iter()
1324 .map(|&x| x.into())
1325 .collect::<Vec<String>>();
1326 println!("{:?}", command_line_flags);
1327
1328 assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
1329}
1330
1331#[test]
1332fn test_rust_to_clang_target() {
1333 assert_eq!(
1334 rust_to_clang_target("aarch64-apple-ios").as_ref(),
1335 "arm64-apple-ios"
1336 );
1337}
1338
1339#[test]
1340fn test_rust_to_clang_target_riscv() {
1341 assert_eq!(
1342 rust_to_clang_target("riscv64gc-unknown-linux-gnu").as_ref(),
1343 "riscv64-unknown-linux-gnu"
1344 );
1345 assert_eq!(
1346 rust_to_clang_target("riscv64imac-unknown-none-elf").as_ref(),
1347 "riscv64-unknown-none-elf"
1348 );
1349 assert_eq!(
1350 rust_to_clang_target("riscv32imc-unknown-none-elf").as_ref(),
1351 "riscv32-unknown-none-elf"
1352 );
1353 assert_eq!(
1354 rust_to_clang_target("riscv32imac-unknown-none-elf").as_ref(),
1355 "riscv32-unknown-none-elf"
1356 );
1357 assert_eq!(
1358 rust_to_clang_target("riscv32imafc-unknown-none-elf").as_ref(),
1359 "riscv32-unknown-none-elf"
1360 );
1361 assert_eq!(
1362 rust_to_clang_target("riscv32i-unknown-none-elf").as_ref(),
1363 "riscv32-unknown-none-elf"
1364 );
1365}
1366
1367#[test]
1368fn test_rust_to_clang_target_espidf() {
1369 assert_eq!(
1370 rust_to_clang_target("riscv32imc-esp-espidf").as_ref(),
1371 "riscv32-esp-elf"
1372 );
1373 assert_eq!(
1374 rust_to_clang_target("xtensa-esp32-espidf").as_ref(),
1375 "xtensa-esp32-elf"
1376 );
1377}
1378