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