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