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