autocxx_bindgen/
lib.rs

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