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