1#![deny(missing_docs)]
14#![deny(unused_extern_crates)]
15#![deny(clippy::disallowed_methods)]
16#![allow(non_upper_case_globals)]
19#![recursion_limit = "128"]
21
22#[macro_use]
23extern crate bitflags;
24#[macro_use]
25extern crate quote;
26
27#[cfg(feature = "logging")]
28#[macro_use]
29extern crate log;
30
31#[cfg(not(feature = "logging"))]
32#[macro_use]
33mod log_stubs;
34
35#[macro_use]
36mod extra_assertions;
37
38mod codegen;
39mod deps;
40mod options;
41mod time;
42
43pub mod callbacks;
44
45mod clang;
46#[cfg(feature = "experimental")]
47mod diagnostics;
48mod features;
49mod ir;
50mod parse;
51mod regex_set;
52
53pub use codegen::{
54 AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
55};
56pub use features::{RustEdition, RustTarget, LATEST_STABLE_RUST};
57pub use ir::annotations::FieldVisibilityKind;
58pub use ir::function::Abi;
59#[cfg(feature = "__cli")]
60pub use options::cli::builder_from_flags;
61
62use codegen::CodegenError;
63use features::RustFeatures;
64use ir::comment;
65use ir::context::{BindgenContext, ItemId};
66use ir::item::Item;
67use options::BindgenOptions;
68use parse::ParseError;
69
70use std::borrow::Cow;
71use std::collections::hash_map::Entry;
72use std::env;
73use std::ffi::OsStr;
74use std::fs::{File, OpenOptions};
75use std::io::{self, Write};
76use std::mem::size_of;
77use std::path::{Path, PathBuf};
78use std::process::{Command, Stdio};
79use std::rc::Rc;
80use std::str::FromStr;
81
82type HashMap<K, V> = rustc_hash::FxHashMap<K, V>;
84type HashSet<K> = rustc_hash::FxHashSet<K>;
85
86pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
88
89const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern";
90
91fn file_is_cpp(name_file: &str) -> bool {
92 Path::new(name_file).extension().map_or(false, |ext| {
93 ext.eq_ignore_ascii_case("hpp") ||
94 ext.eq_ignore_ascii_case("hxx") ||
95 ext.eq_ignore_ascii_case("hh") ||
96 ext.eq_ignore_ascii_case("h++")
97 })
98}
99
100fn args_are_cpp(clang_args: &[Box<str>]) -> bool {
101 for w in clang_args.windows(2) {
102 if w[0].as_ref() == "-xc++" || w[1].as_ref() == "-xc++" {
103 return true;
104 }
105 if w[0].as_ref() == "-x" && w[1].as_ref() == "c++" {
106 return true;
107 }
108 if w[0].as_ref() == "-include" && file_is_cpp(w[1].as_ref()) {
109 return true;
110 }
111 }
112 false
113}
114
115bitflags! {
116 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
118 pub struct CodegenConfig: u32 {
119 const FUNCTIONS = 1 << 0;
121 const TYPES = 1 << 1;
123 const VARS = 1 << 2;
125 const METHODS = 1 << 3;
127 const CONSTRUCTORS = 1 << 4;
129 const DESTRUCTORS = 1 << 5;
131 }
132}
133
134impl CodegenConfig {
135 pub fn functions(self) -> bool {
137 self.contains(CodegenConfig::FUNCTIONS)
138 }
139
140 pub fn types(self) -> bool {
142 self.contains(CodegenConfig::TYPES)
143 }
144
145 pub fn vars(self) -> bool {
147 self.contains(CodegenConfig::VARS)
148 }
149
150 pub fn methods(self) -> bool {
152 self.contains(CodegenConfig::METHODS)
153 }
154
155 pub fn constructors(self) -> bool {
157 self.contains(CodegenConfig::CONSTRUCTORS)
158 }
159
160 pub fn destructors(self) -> bool {
162 self.contains(CodegenConfig::DESTRUCTORS)
163 }
164}
165
166impl Default for CodegenConfig {
167 fn default() -> Self {
168 CodegenConfig::all()
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174#[non_exhaustive]
175pub enum Formatter {
176 None,
178 Rustfmt,
180 #[cfg(feature = "prettyplease")]
181 Prettyplease,
183}
184
185impl Default for Formatter {
186 fn default() -> Self {
187 Self::Rustfmt
188 }
189}
190
191impl FromStr for Formatter {
192 type Err = String;
193
194 fn from_str(s: &str) -> Result<Self, Self::Err> {
195 match s {
196 "none" => Ok(Self::None),
197 "rustfmt" => Ok(Self::Rustfmt),
198 #[cfg(feature = "prettyplease")]
199 "prettyplease" => Ok(Self::Prettyplease),
200 _ => Err(format!("`{s}` is not a valid formatter")),
201 }
202 }
203}
204
205impl std::fmt::Display for Formatter {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 let s = match self {
208 Self::None => "none",
209 Self::Rustfmt => "rustfmt",
210 #[cfg(feature = "prettyplease")]
211 Self::Prettyplease => "prettyplease",
212 };
213
214 std::fmt::Display::fmt(&s, f)
215 }
216}
217
218#[derive(Debug, Default, Clone)]
294pub struct Builder {
295 options: BindgenOptions,
296}
297
298pub fn builder() -> Builder {
300 Default::default()
301}
302
303fn get_extra_clang_args(
304 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
305) -> Vec<String> {
306 let extra_clang_args = match get_target_dependent_env_var(
308 parse_callbacks,
309 "BINDGEN_EXTRA_CLANG_ARGS",
310 ) {
311 None => return vec![],
312 Some(s) => s,
313 };
314
315 if let Some(strings) = shlex::split(&extra_clang_args) {
317 return strings;
318 }
319 vec![extra_clang_args]
320}
321
322impl Builder {
323 pub fn generate(mut self) -> Result<Bindings, BindgenError> {
325 self.options.rust_features = match self.options.rust_edition {
327 Some(edition) => {
328 if !edition.is_available(self.options.rust_target) {
329 return Err(BindgenError::UnsupportedEdition(
330 edition,
331 self.options.rust_target,
332 ));
333 }
334 RustFeatures::new(self.options.rust_target, edition)
335 }
336 None => {
337 RustFeatures::new_with_latest_edition(self.options.rust_target)
338 }
339 };
340
341 self.options.clang_args.extend(
343 get_extra_clang_args(&self.options.parse_callbacks)
344 .into_iter()
345 .map(String::into_boxed_str),
346 );
347
348 for header in &self.options.input_headers {
349 self.options
350 .for_each_callback(|cb| cb.header_file(header.as_ref()));
351 }
352
353 self.options.fallback_clang_args = self
355 .options
356 .clang_args
357 .iter()
358 .filter(|arg| {
359 !arg.starts_with("-MMD") &&
360 !arg.starts_with("-MD") &&
361 !arg.starts_with("--write-user-dependencies") &&
362 !arg.starts_with("--user-dependencies")
363 })
364 .cloned()
365 .collect::<Vec<_>>();
366 self.options.clang_args.extend(
367 self.options.input_headers
368 [..self.options.input_headers.len().saturating_sub(1)]
369 .iter()
370 .flat_map(|header| ["-include".into(), header.clone()]),
371 );
372
373 let input_unsaved_files =
374 std::mem::take(&mut self.options.input_header_contents)
375 .into_iter()
376 .map(|(name, contents)| {
377 clang::UnsavedFile::new(name.as_ref(), contents.as_ref())
378 })
379 .collect::<Vec<_>>();
380
381 Bindings::generate(self.options, &input_unsaved_files)
382 }
383
384 pub fn dump_preprocessed_input(&self) -> io::Result<()> {
390 let clang =
391 clang_sys::support::Clang::find(None, &[]).ok_or_else(|| {
392 io::Error::new(
393 io::ErrorKind::Other,
394 "Cannot find clang executable",
395 )
396 })?;
397
398 let mut wrapper_contents = String::new();
401
402 let mut is_cpp = args_are_cpp(&self.options.clang_args);
404
405 for header in &self.options.input_headers {
407 is_cpp |= file_is_cpp(header);
408
409 wrapper_contents.push_str("#include \"");
410 wrapper_contents.push_str(header);
411 wrapper_contents.push_str("\"\n");
412 }
413
414 for (name, contents) in &self.options.input_header_contents {
417 is_cpp |= file_is_cpp(name);
418
419 wrapper_contents.push_str("#line 0 \"");
420 wrapper_contents.push_str(name);
421 wrapper_contents.push_str("\"\n");
422 wrapper_contents.push_str(contents);
423 }
424
425 let wrapper_path = PathBuf::from(if is_cpp {
426 "__bindgen.cpp"
427 } else {
428 "__bindgen.c"
429 });
430
431 {
432 let mut wrapper_file = File::create(&wrapper_path)?;
433 wrapper_file.write_all(wrapper_contents.as_bytes())?;
434 }
435
436 let mut cmd = Command::new(clang.path);
437 cmd.arg("-save-temps")
438 .arg("-E")
439 .arg("-C")
440 .arg("-c")
441 .arg(&wrapper_path)
442 .stdout(Stdio::piped());
443
444 for a in &self.options.clang_args {
445 cmd.arg(a.as_ref());
446 }
447
448 for a in get_extra_clang_args(&self.options.parse_callbacks) {
449 cmd.arg(a);
450 }
451
452 let mut child = cmd.spawn()?;
453
454 let mut preprocessed = child.stdout.take().unwrap();
455 let mut file = File::create(if is_cpp {
456 "__bindgen.ii"
457 } else {
458 "__bindgen.i"
459 })?;
460 io::copy(&mut preprocessed, &mut file)?;
461
462 if child.wait()?.success() {
463 Ok(())
464 } else {
465 Err(io::Error::new(
466 io::ErrorKind::Other,
467 "clang exited with non-zero status",
468 ))
469 }
470 }
471}
472
473impl BindgenOptions {
474 fn build(&mut self) {
475 const REGEX_SETS_LEN: usize = 29;
476
477 let regex_sets: [_; REGEX_SETS_LEN] = [
478 &mut self.blocklisted_types,
479 &mut self.blocklisted_functions,
480 &mut self.blocklisted_items,
481 &mut self.blocklisted_files,
482 &mut self.blocklisted_vars,
483 &mut self.opaque_types,
484 &mut self.allowlisted_vars,
485 &mut self.allowlisted_types,
486 &mut self.allowlisted_functions,
487 &mut self.allowlisted_files,
488 &mut self.allowlisted_items,
489 &mut self.bitfield_enums,
490 &mut self.constified_enums,
491 &mut self.constified_enum_modules,
492 &mut self.newtype_enums,
493 &mut self.newtype_global_enums,
494 &mut self.rustified_enums,
495 &mut self.rustified_non_exhaustive_enums,
496 &mut self.type_alias,
497 &mut self.new_type_alias,
498 &mut self.new_type_alias_deref,
499 &mut self.bindgen_wrapper_union,
500 &mut self.manually_drop_union,
501 &mut self.no_partialeq_types,
502 &mut self.no_copy_types,
503 &mut self.no_debug_types,
504 &mut self.no_default_types,
505 &mut self.no_hash_types,
506 &mut self.must_use_types,
507 ];
508
509 let record_matches = self.record_matches;
510 #[cfg(feature = "experimental")]
511 {
512 let sets_len = REGEX_SETS_LEN + self.abi_overrides.len();
513 let names = if self.emit_diagnostics {
514 <[&str; REGEX_SETS_LEN]>::into_iter([
515 "--blocklist-type",
516 "--blocklist-function",
517 "--blocklist-item",
518 "--blocklist-file",
519 "--blocklist-var",
520 "--opaque-type",
521 "--allowlist-type",
522 "--allowlist-function",
523 "--allowlist-var",
524 "--allowlist-file",
525 "--allowlist-item",
526 "--bitfield-enum",
527 "--newtype-enum",
528 "--newtype-global-enum",
529 "--rustified-enum",
530 "--rustified-enum-non-exhaustive",
531 "--constified-enum-module",
532 "--constified-enum",
533 "--type-alias",
534 "--new-type-alias",
535 "--new-type-alias-deref",
536 "--bindgen-wrapper-union",
537 "--manually-drop-union",
538 "--no-partialeq",
539 "--no-copy",
540 "--no-debug",
541 "--no-default",
542 "--no-hash",
543 "--must-use",
544 ])
545 .chain((0..self.abi_overrides.len()).map(|_| "--override-abi"))
546 .map(Some)
547 .collect()
548 } else {
549 vec![None; sets_len]
550 };
551
552 for (regex_set, name) in
553 self.abi_overrides.values_mut().chain(regex_sets).zip(names)
554 {
555 regex_set.build_with_diagnostics(record_matches, name);
556 }
557 }
558 #[cfg(not(feature = "experimental"))]
559 for regex_set in self.abi_overrides.values_mut().chain(regex_sets) {
560 regex_set.build(record_matches);
561 }
562 }
563
564 pub fn set_rust_target(&mut self, rust_target: RustTarget) {
566 self.rust_target = rust_target;
567 }
568
569 pub fn rust_features(&self) -> RustFeatures {
571 self.rust_features
572 }
573
574 fn last_callback<T>(
575 &self,
576 f: impl Fn(&dyn callbacks::ParseCallbacks) -> Option<T>,
577 ) -> Option<T> {
578 self.parse_callbacks
579 .iter()
580 .filter_map(|cb| f(cb.as_ref()))
581 .last()
582 }
583
584 fn all_callbacks<T>(
585 &self,
586 f: impl Fn(&dyn callbacks::ParseCallbacks) -> Vec<T>,
587 ) -> Vec<T> {
588 self.parse_callbacks
589 .iter()
590 .flat_map(|cb| f(cb.as_ref()))
591 .collect()
592 }
593
594 fn for_each_callback(&self, f: impl Fn(&dyn callbacks::ParseCallbacks)) {
595 self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref()));
596 }
597
598 fn process_comment(&self, comment: &str) -> String {
599 let comment = comment::preprocess(comment);
600 self.last_callback(|cb| cb.process_comment(&comment))
601 .unwrap_or(comment)
602 }
603}
604
605#[cfg(feature = "runtime")]
606fn ensure_libclang_is_loaded() {
607 use std::sync::{Arc, OnceLock};
608
609 if clang_sys::is_loaded() {
610 return;
611 }
612
613 static LIBCLANG: OnceLock<Arc<clang_sys::SharedLibrary>> = OnceLock::new();
618 let libclang = LIBCLANG.get_or_init(|| {
619 clang_sys::load().expect("Unable to find libclang");
620 clang_sys::get_library()
621 .expect("We just loaded libclang and it had better still be here!")
622 });
623
624 clang_sys::set_library(Some(libclang.clone()));
625}
626
627#[cfg(not(feature = "runtime"))]
628fn ensure_libclang_is_loaded() {}
629
630#[derive(Debug, Clone, PartialEq, Eq, Hash)]
632#[non_exhaustive]
633pub enum BindgenError {
634 FolderAsHeader(PathBuf),
636 InsufficientPermissions(PathBuf),
638 NotExist(PathBuf),
640 ClangDiagnostic(String),
642 Codegen(CodegenError),
644 UnsupportedEdition(RustEdition, RustTarget),
646}
647
648impl std::fmt::Display for BindgenError {
649 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
650 match self {
651 BindgenError::FolderAsHeader(h) => {
652 write!(f, "'{}' is a folder", h.display())
653 }
654 BindgenError::InsufficientPermissions(h) => {
655 write!(f, "insufficient permissions to read '{}'", h.display())
656 }
657 BindgenError::NotExist(h) => {
658 write!(f, "header '{}' does not exist.", h.display())
659 }
660 BindgenError::ClangDiagnostic(message) => {
661 write!(f, "clang diagnosed error: {message}")
662 }
663 BindgenError::Codegen(err) => {
664 write!(f, "codegen error: {err}")
665 }
666 BindgenError::UnsupportedEdition(edition, target) => {
667 write!(f, "edition {edition} is not available on Rust {target}")
668 }
669 }
670 }
671}
672
673impl std::error::Error for BindgenError {}
674
675#[derive(Debug)]
677pub struct Bindings {
678 options: BindgenOptions,
679 module: proc_macro2::TokenStream,
680}
681
682pub(crate) const HOST_TARGET: &str =
683 include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
684
685fn rust_to_clang_target(rust_target: &str) -> Box<str> {
688 const TRIPLE_HYPHENS_MESSAGE: &str = "Target triple should contain hyphens";
689
690 let mut clang_target = rust_target.to_owned();
691
692 if clang_target.starts_with("riscv32") {
693 let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
694
695 clang_target.replace_range(..idx, "riscv32");
696 } else if clang_target.starts_with("riscv64") {
697 let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
698
699 clang_target.replace_range(..idx, "riscv64");
700 } else if clang_target.starts_with("aarch64-apple-") {
701 let idx = clang_target.find('-').expect(TRIPLE_HYPHENS_MESSAGE);
702
703 clang_target.replace_range(..idx, "arm64");
704 }
705
706 if clang_target.ends_with("-espidf") {
707 let idx = clang_target.rfind('-').expect(TRIPLE_HYPHENS_MESSAGE);
708
709 clang_target.replace_range((idx + 1).., "elf");
710 }
711
712 clang_target.into()
713}
714
715fn find_effective_target(clang_args: &[Box<str>]) -> (Box<str>, bool) {
718 let mut args = clang_args.iter();
719 while let Some(opt) = args.next() {
720 if opt.starts_with("--target=") {
721 let mut split = opt.split('=');
722 split.next();
723 return (split.next().unwrap().into(), true);
724 }
725
726 if opt.as_ref() == "-target" {
727 if let Some(target) = args.next() {
728 return (target.clone(), true);
729 }
730 }
731 }
732
733 if let Ok(t) = env::var("TARGET") {
735 return (rust_to_clang_target(&t), false);
736 }
737
738 (rust_to_clang_target(HOST_TARGET), false)
739}
740
741impl Bindings {
742 pub(crate) fn generate(
744 mut options: BindgenOptions,
745 input_unsaved_files: &[clang::UnsavedFile],
746 ) -> Result<Bindings, BindgenError> {
747 ensure_libclang_is_loaded();
748
749 #[cfg(feature = "runtime")]
750 match clang_sys::get_library().unwrap().version() {
751 None => {
752 warn!("Could not detect a Clang version, make sure you are using libclang 9 or newer");
753 }
754 Some(version) => {
755 if version < clang_sys::Version::V9_0 {
756 warn!("Detected Clang version {version:?} which is unsupported and can cause invalid code generation, use libclang 9 or newer");
757 }
758 }
759 }
760
761 #[cfg(feature = "runtime")]
762 debug!(
763 "Generating bindings, libclang at {}",
764 clang_sys::get_library().unwrap().path().display()
765 );
766 #[cfg(not(feature = "runtime"))]
767 debug!("Generating bindings, libclang linked");
768
769 options.build();
770
771 let (effective_target, explicit_target) =
772 find_effective_target(&options.clang_args);
773
774 let is_host_build =
775 rust_to_clang_target(HOST_TARGET) == effective_target;
776
777 if !explicit_target && !is_host_build {
783 options.clang_args.insert(
784 0,
785 format!("--target={effective_target}").into_boxed_str(),
786 );
787 };
788
789 fn detect_include_paths(options: &mut BindgenOptions) {
790 if !options.detect_include_paths {
791 return;
792 }
793
794 let clang_args_for_clang_sys = {
797 let mut last_was_include_prefix = false;
798 options
799 .clang_args
800 .iter()
801 .filter(|arg| {
802 if last_was_include_prefix {
803 last_was_include_prefix = false;
804 return false;
805 }
806
807 let arg = arg.as_ref();
808
809 if arg == "-I" || arg == "--include-directory" {
812 last_was_include_prefix = true;
813 return false;
814 }
815
816 if arg.starts_with("-I") ||
817 arg.starts_with("--include-directory=")
818 {
819 return false;
820 }
821
822 true
823 })
824 .map(|arg| arg.clone().into())
825 .collect::<Vec<_>>()
826 };
827
828 debug!(
829 "Trying to find clang with flags: {clang_args_for_clang_sys:?}"
830 );
831
832 let clang = match clang_sys::support::Clang::find(
833 None,
834 &clang_args_for_clang_sys,
835 ) {
836 None => return,
837 Some(clang) => clang,
838 };
839
840 debug!("Found clang: {clang:?}");
841
842 let is_cpp = args_are_cpp(&options.clang_args) ||
844 options.input_headers.iter().any(|h| file_is_cpp(h));
845
846 let search_paths = if is_cpp {
847 clang.cpp_search_paths
848 } else {
849 clang.c_search_paths
850 };
851
852 if let Some(search_paths) = search_paths {
853 for path in search_paths {
854 if let Ok(path) = path.into_os_string().into_string() {
855 options.clang_args.push("-isystem".into());
856 options.clang_args.push(path.into_boxed_str());
857 }
858 }
859 }
860 }
861
862 detect_include_paths(&mut options);
863
864 #[cfg(unix)]
865 fn can_read(perms: &std::fs::Permissions) -> bool {
866 use std::os::unix::fs::PermissionsExt;
867 perms.mode() & 0o444 > 0
868 }
869
870 #[cfg(not(unix))]
871 fn can_read(_: &std::fs::Permissions) -> bool {
872 true
873 }
874
875 if let Some(h) = options.input_headers.last() {
876 let path = Path::new(h.as_ref());
877 if let Ok(md) = std::fs::metadata(path) {
878 if md.is_dir() {
879 return Err(BindgenError::FolderAsHeader(path.into()));
880 }
881 if !can_read(&md.permissions()) {
882 return Err(BindgenError::InsufficientPermissions(
883 path.into(),
884 ));
885 }
886 options.clang_args.push(h.clone());
887 } else {
888 return Err(BindgenError::NotExist(path.into()));
889 }
890 }
891
892 for (idx, f) in input_unsaved_files.iter().enumerate() {
893 if idx != 0 || !options.input_headers.is_empty() {
894 options.clang_args.push("-include".into());
895 }
896 options.clang_args.push(f.name.to_str().unwrap().into());
897 }
898
899 debug!("Fixed-up options: {options:?}");
900
901 let time_phases = options.time_phases;
902 let mut context = BindgenContext::new(options, input_unsaved_files);
903
904 if is_host_build {
905 debug_assert_eq!(
906 context.target_pointer_size(),
907 size_of::<*mut ()>(),
908 "{effective_target:?} {HOST_TARGET:?}"
909 );
910 }
911
912 {
913 let _t = time::Timer::new("parse").with_output(time_phases);
914 parse(&mut context)?;
915 }
916
917 let (module, options) =
918 codegen::codegen(context).map_err(BindgenError::Codegen)?;
919
920 Ok(Bindings { options, module })
921 }
922
923 pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
925 let file = OpenOptions::new()
926 .write(true)
927 .truncate(true)
928 .create(true)
929 .open(path.as_ref())?;
930 self.write(Box::new(file))?;
931 Ok(())
932 }
933
934 pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> {
936 const NL: &str = if cfg!(windows) { "\r\n" } else { "\n" };
937
938 if !self.options.disable_header_comment {
939 let version =
940 option_env!("CARGO_PKG_VERSION").unwrap_or("(unknown version)");
941 write!(
942 writer,
943 "/* automatically generated by rust-bindgen {version} */{NL}{NL}",
944 )?;
945 }
946
947 for line in &self.options.raw_lines {
948 writer.write_all(line.as_bytes())?;
949 writer.write_all(NL.as_bytes())?;
950 }
951
952 if !self.options.raw_lines.is_empty() {
953 writer.write_all(NL.as_bytes())?;
954 }
955
956 match self.format_tokens(&self.module) {
957 Ok(formatted_bindings) => {
958 writer.write_all(formatted_bindings.as_bytes())?;
959 }
960 Err(err) => {
961 eprintln!(
962 "Failed to run rustfmt: {err} (non-fatal, continuing)"
963 );
964 writer.write_all(self.module.to_string().as_bytes())?;
965 }
966 }
967 Ok(())
968 }
969
970 fn rustfmt_path(&self) -> io::Result<Cow<PathBuf>> {
972 debug_assert!(matches!(self.options.formatter, Formatter::Rustfmt));
973 if let Some(ref p) = self.options.rustfmt_path {
974 return Ok(Cow::Borrowed(p));
975 }
976 if let Ok(rustfmt) = env::var("RUSTFMT") {
977 return Ok(Cow::Owned(rustfmt.into()));
978 }
979 Ok(Cow::Owned("rustfmt".into()))
982 }
983
984 fn format_tokens(
986 &self,
987 tokens: &proc_macro2::TokenStream,
988 ) -> io::Result<String> {
989 let _t = time::Timer::new("rustfmt_generated_string")
990 .with_output(self.options.time_phases);
991
992 match self.options.formatter {
993 Formatter::None => return Ok(tokens.to_string()),
994 #[cfg(feature = "prettyplease")]
995 Formatter::Prettyplease => {
996 return Ok(prettyplease::unparse(&syn::parse_quote!(#tokens)));
997 }
998 Formatter::Rustfmt => (),
999 }
1000
1001 let rustfmt = self.rustfmt_path()?;
1002 let mut cmd = Command::new(&*rustfmt);
1003
1004 cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
1005
1006 if let Some(path) = self
1007 .options
1008 .rustfmt_configuration_file
1009 .as_ref()
1010 .and_then(|f| f.to_str())
1011 {
1012 cmd.args(["--config-path", path]);
1013 }
1014
1015 let edition = self
1016 .options
1017 .rust_edition
1018 .unwrap_or_else(|| self.options.rust_target.latest_edition());
1019 cmd.args(["--edition", &format!("{edition}")]);
1020
1021 let mut child = cmd.spawn()?;
1022 let mut child_stdin = child.stdin.take().unwrap();
1023 let mut child_stdout = child.stdout.take().unwrap();
1024
1025 let source = tokens.to_string();
1026
1027 let stdin_handle = ::std::thread::spawn(move || {
1031 let _ = child_stdin.write_all(source.as_bytes());
1032 source
1033 });
1034
1035 let mut output = vec![];
1036 io::copy(&mut child_stdout, &mut output)?;
1037
1038 let status = child.wait()?;
1039 let source = stdin_handle.join().expect(
1040 "The thread writing to rustfmt's stdin doesn't do \
1041 anything that could panic",
1042 );
1043
1044 match String::from_utf8(output) {
1045 Ok(bindings) => match status.code() {
1046 Some(0) => Ok(bindings),
1047 Some(2) => Err(io::Error::new(
1048 io::ErrorKind::Other,
1049 "Rustfmt parsing errors.".to_string(),
1050 )),
1051 Some(3) => {
1052 rustfmt_non_fatal_error_diagnostic(
1053 "Rustfmt could not format some lines",
1054 &self.options,
1055 );
1056 Ok(bindings)
1057 }
1058 _ => Err(io::Error::new(
1059 io::ErrorKind::Other,
1060 "Internal rustfmt error".to_string(),
1061 )),
1062 },
1063 _ => Ok(source),
1064 }
1065 }
1066}
1067
1068fn rustfmt_non_fatal_error_diagnostic(msg: &str, _options: &BindgenOptions) {
1069 warn!("{msg}");
1070
1071 #[cfg(feature = "experimental")]
1072 if _options.emit_diagnostics {
1073 use crate::diagnostics::{Diagnostic, Level};
1074
1075 Diagnostic::default()
1076 .with_title(msg, Level::Warning)
1077 .add_annotation(
1078 "The bindings will be generated but not formatted.",
1079 Level::Note,
1080 )
1081 .display();
1082 }
1083}
1084
1085impl std::fmt::Display for Bindings {
1086 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1087 let mut bytes = vec![];
1088 self.write(Box::new(&mut bytes) as Box<dyn Write>)
1089 .expect("writing to a vec cannot fail");
1090 f.write_str(
1091 std::str::from_utf8(&bytes)
1092 .expect("we should only write bindings that are valid utf-8"),
1093 )
1094 }
1095}
1096
1097fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
1100 ctx.options().builtins || !cursor.is_builtin()
1101}
1102
1103fn parse_one(
1105 ctx: &mut BindgenContext,
1106 cursor: clang::Cursor,
1107 parent: Option<ItemId>,
1108) {
1109 if !filter_builtins(ctx, &cursor) {
1110 return;
1111 }
1112
1113 match Item::parse(cursor, parent, ctx) {
1114 Ok(..) => {}
1115 Err(ParseError::Continue) => {}
1116 Err(ParseError::Recurse) => {
1117 cursor
1118 .visit_sorted(ctx, |ctx, child| parse_one(ctx, child, parent));
1119 }
1120 }
1121}
1122
1123fn parse(context: &mut BindgenContext) -> Result<(), BindgenError> {
1125 use clang_sys::*;
1126
1127 let mut error = None;
1128 for d in &context.translation_unit().diags() {
1129 let msg = d.format();
1130 let is_err = d.severity() >= CXDiagnostic_Error;
1131 if is_err {
1132 let error = error.get_or_insert_with(String::new);
1133 error.push_str(&msg);
1134 error.push('\n');
1135 } else {
1136 eprintln!("clang diag: {msg}");
1137 }
1138 }
1139
1140 if let Some(message) = error {
1141 return Err(BindgenError::ClangDiagnostic(message));
1142 }
1143
1144 let cursor = context.translation_unit().cursor();
1145
1146 if context.options().emit_ast {
1147 fn dump_if_not_builtin(cur: &clang::Cursor) -> CXChildVisitResult {
1148 if cur.is_builtin() {
1149 CXChildVisit_Continue
1150 } else {
1151 clang::ast_dump(cur, 0)
1152 }
1153 }
1154 cursor.visit(|cur| dump_if_not_builtin(&cur));
1155 }
1156
1157 let root = context.root_module();
1158 context.with_module(root, |ctx| {
1159 cursor.visit_sorted(ctx, |ctx, child| parse_one(ctx, child, None));
1160 });
1161
1162 assert_eq!(
1163 context.current_module(),
1164 context.root_module(),
1165 "How did this happen?"
1166 );
1167 Ok(())
1168}
1169
1170#[derive(Debug)]
1172pub struct ClangVersion {
1173 pub parsed: Option<(u32, u32)>,
1175 pub full: String,
1177}
1178
1179pub fn clang_version() -> ClangVersion {
1181 ensure_libclang_is_loaded();
1182
1183 let raw_v: String = clang::extract_clang_version();
1185 let split_v: Option<Vec<&str>> = raw_v
1186 .split_whitespace()
1187 .find(|t| t.chars().next().is_some_and(|v| v.is_ascii_digit()))
1188 .map(|v| v.split('.').collect());
1189 if let Some(v) = split_v {
1190 if v.len() >= 2 {
1191 let maybe_major = v[0].parse::<u32>();
1192 let maybe_minor = v[1].parse::<u32>();
1193 if let (Ok(major), Ok(minor)) = (maybe_major, maybe_minor) {
1194 return ClangVersion {
1195 parsed: Some((major, minor)),
1196 full: raw_v.clone(),
1197 };
1198 }
1199 }
1200 };
1201 ClangVersion {
1202 parsed: None,
1203 full: raw_v.clone(),
1204 }
1205}
1206
1207fn env_var<K: AsRef<str> + AsRef<OsStr>>(
1208 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1209 key: K,
1210) -> Result<String, env::VarError> {
1211 for callback in parse_callbacks {
1212 callback.read_env_var(key.as_ref());
1213 }
1214 env::var(key)
1215}
1216
1217fn get_target_dependent_env_var(
1219 parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1220 var: &str,
1221) -> Option<String> {
1222 if let Ok(target) = env_var(parse_callbacks, "TARGET") {
1223 if let Ok(v) = env_var(parse_callbacks, format!("{var}_{target}")) {
1224 return Some(v);
1225 }
1226 if let Ok(v) = env_var(
1227 parse_callbacks,
1228 format!("{var}_{}", target.replace('-', "_")),
1229 ) {
1230 return Some(v);
1231 }
1232 }
1233
1234 env_var(parse_callbacks, var).ok()
1235}
1236
1237#[derive(Debug)]
1251pub struct CargoCallbacks {
1252 rerun_on_header_files: bool,
1253}
1254
1255#[deprecated = "Use `CargoCallbacks::new()` instead. Please, check the documentation for further information."]
1260pub const CargoCallbacks: CargoCallbacks = CargoCallbacks {
1261 rerun_on_header_files: false,
1262};
1263
1264impl CargoCallbacks {
1265 pub fn new() -> Self {
1267 Self {
1268 rerun_on_header_files: true,
1269 }
1270 }
1271
1272 pub fn rerun_on_header_files(mut self, doit: bool) -> Self {
1277 self.rerun_on_header_files = doit;
1278 self
1279 }
1280}
1281
1282impl Default for CargoCallbacks {
1283 fn default() -> Self {
1284 Self::new()
1285 }
1286}
1287
1288impl callbacks::ParseCallbacks for CargoCallbacks {
1289 fn header_file(&self, filename: &str) {
1290 if self.rerun_on_header_files {
1291 println!("cargo:rerun-if-changed={filename}");
1292 }
1293 }
1294
1295 fn include_file(&self, filename: &str) {
1296 println!("cargo:rerun-if-changed={filename}");
1297 }
1298
1299 fn read_env_var(&self, key: &str) {
1300 println!("cargo:rerun-if-env-changed={key}");
1301 }
1302}
1303
1304#[test]
1306fn commandline_flag_unit_test_function() {
1307 let bindings = builder();
1309 let command_line_flags = bindings.command_line_flags();
1310
1311 let test_cases = [
1312 "--rust-target",
1313 "--no-derive-default",
1314 "--generate",
1315 "functions,types,vars,methods,constructors,destructors",
1316 ]
1317 .iter()
1318 .map(|&x| x.into())
1319 .collect::<Vec<String>>();
1320
1321 assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
1322
1323 let bindings = builder()
1325 .header("input_header")
1326 .allowlist_type("Distinct_Type")
1327 .allowlist_function("safe_function");
1328
1329 let command_line_flags = bindings.command_line_flags();
1330 let test_cases = [
1331 "--rust-target",
1332 "input_header",
1333 "--no-derive-default",
1334 "--generate",
1335 "functions,types,vars,methods,constructors,destructors",
1336 "--allowlist-type",
1337 "Distinct_Type",
1338 "--allowlist-function",
1339 "safe_function",
1340 ]
1341 .iter()
1342 .map(|&x| x.into())
1343 .collect::<Vec<String>>();
1344 println!("{command_line_flags:?}");
1345
1346 assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
1347}
1348
1349#[test]
1350fn test_rust_to_clang_target() {
1351 assert_eq!(
1352 rust_to_clang_target("aarch64-apple-ios").as_ref(),
1353 "arm64-apple-ios"
1354 );
1355}
1356
1357#[test]
1358fn test_rust_to_clang_target_riscv() {
1359 assert_eq!(
1360 rust_to_clang_target("riscv64gc-unknown-linux-gnu").as_ref(),
1361 "riscv64-unknown-linux-gnu"
1362 );
1363 assert_eq!(
1364 rust_to_clang_target("riscv64imac-unknown-none-elf").as_ref(),
1365 "riscv64-unknown-none-elf"
1366 );
1367 assert_eq!(
1368 rust_to_clang_target("riscv32imc-unknown-none-elf").as_ref(),
1369 "riscv32-unknown-none-elf"
1370 );
1371 assert_eq!(
1372 rust_to_clang_target("riscv32imac-unknown-none-elf").as_ref(),
1373 "riscv32-unknown-none-elf"
1374 );
1375 assert_eq!(
1376 rust_to_clang_target("riscv32imafc-unknown-none-elf").as_ref(),
1377 "riscv32-unknown-none-elf"
1378 );
1379 assert_eq!(
1380 rust_to_clang_target("riscv32i-unknown-none-elf").as_ref(),
1381 "riscv32-unknown-none-elf"
1382 );
1383}
1384
1385#[test]
1386fn test_rust_to_clang_target_espidf() {
1387 assert_eq!(
1388 rust_to_clang_target("riscv32imc-esp-espidf").as_ref(),
1389 "riscv32-esp-elf"
1390 );
1391 assert_eq!(
1392 rust_to_clang_target("xtensa-esp32-espidf").as_ref(),
1393 "xtensa-esp32-elf"
1394 );
1395}