bindgen/
lib.rs

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