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