#![deny(missing_docs)]
#![deny(unused_extern_crates)]
#![allow(non_upper_case_globals)]
#![recursion_limit = "128"]
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate quote;
#[cfg(feature = "logging")]
#[macro_use]
extern crate log;
#[cfg(not(feature = "logging"))]
#[macro_use]
mod log_stubs;
#[macro_use]
mod extra_assertions;
macro_rules! doc_mod {
($m:ident, $doc_mod_name:ident) => {
#[cfg(feature = "testing_only_docs")]
pub mod $doc_mod_name {
pub use super::$m::*;
}
};
}
macro_rules! fn_with_regex_arg {
($(#[$attrs:meta])* pub fn $($tokens:tt)*) => {
$(#[$attrs])*
pub fn $($tokens)*
};
}
mod clang;
mod codegen;
mod deps;
mod features;
pub mod ir;
mod parse;
mod regex_set;
mod time;
pub mod callbacks;
doc_mod!(clang, clang_docs);
doc_mod!(features, features_docs);
doc_mod!(ir, ir_docs);
doc_mod!(parse, parse_docs);
doc_mod!(regex_set, regex_set_docs);
use codegen::CodegenError;
use ir::comment;
pub use crate::codegen::{
AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
};
use crate::features::RustFeatures;
pub use crate::features::{
RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS,
};
use crate::ir::context::{BindgenContext, ItemId};
pub use crate::ir::function::Abi;
use crate::ir::item::Item;
use crate::parse::ParseError;
pub use crate::regex_set::RegexSet;
use std::borrow::Cow;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::rc::Rc;
type HashMap<K, V> = ::rustc_hash::FxHashMap<K, V>;
type HashSet<K> = ::rustc_hash::FxHashSet<K>;
pub(crate) use std::collections::hash_map::Entry;
pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern";
fn file_is_cpp(name_file: &str) -> bool {
name_file.ends_with(".hpp") ||
name_file.ends_with(".hxx") ||
name_file.ends_with(".hh") ||
name_file.ends_with(".h++")
}
fn args_are_cpp(clang_args: &[String]) -> bool {
for w in clang_args.windows(2) {
if w[0] == "-xc++" || w[1] == "-xc++" {
return true;
}
if w[0] == "-x" && w[1] == "c++" {
return true;
}
if w[0] == "-include" && file_is_cpp(&w[1]) {
return true;
}
}
false
}
bitflags! {
pub struct CodegenConfig: u32 {
const FUNCTIONS = 1 << 0;
const TYPES = 1 << 1;
const VARS = 1 << 2;
const METHODS = 1 << 3;
const CONSTRUCTORS = 1 << 4;
const DESTRUCTORS = 1 << 5;
}
}
impl CodegenConfig {
pub fn functions(self) -> bool {
self.contains(CodegenConfig::FUNCTIONS)
}
pub fn types(self) -> bool {
self.contains(CodegenConfig::TYPES)
}
pub fn vars(self) -> bool {
self.contains(CodegenConfig::VARS)
}
pub fn methods(self) -> bool {
self.contains(CodegenConfig::METHODS)
}
pub fn constructors(self) -> bool {
self.contains(CodegenConfig::CONSTRUCTORS)
}
pub fn destructors(self) -> bool {
self.contains(CodegenConfig::DESTRUCTORS)
}
}
impl Default for CodegenConfig {
fn default() -> Self {
CodegenConfig::all()
}
}
#[derive(Debug, Default, Clone)]
pub struct Builder {
options: BindgenOptions,
}
pub fn builder() -> Builder {
Default::default()
}
fn get_extra_clang_args() -> Vec<String> {
let extra_clang_args =
match get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS") {
None => return vec![],
Some(s) => s,
};
if let Some(strings) = shlex::split(&extra_clang_args) {
return strings;
}
vec![extra_clang_args]
}
impl Builder {
pub fn command_line_flags(&self) -> Vec<String> {
let mut output_vector: Vec<String> = Vec::new();
if let Some(header) = self.options.input_headers.last().cloned() {
output_vector.push(header);
}
output_vector.push("--rust-target".into());
output_vector.push(self.options.rust_target.into());
if !self.options.rust_features.untagged_union &&
RustFeatures::from(self.options.rust_target).untagged_union
{
output_vector.push("--disable-untagged-union".into());
}
if self.options.default_enum_style != Default::default() {
output_vector.push("--default-enum-style".into());
output_vector.push(
match self.options.default_enum_style {
codegen::EnumVariation::Rust {
non_exhaustive: false,
} => "rust",
codegen::EnumVariation::Rust {
non_exhaustive: true,
} => "rust_non_exhaustive",
codegen::EnumVariation::NewType {
is_bitfield: true,
..
} => "bitfield",
codegen::EnumVariation::NewType {
is_bitfield: false,
is_global,
} => {
if is_global {
"newtype_global"
} else {
"newtype"
}
}
codegen::EnumVariation::Consts => "consts",
codegen::EnumVariation::ModuleConsts => "moduleconsts",
}
.into(),
)
}
if self.options.default_macro_constant_type != Default::default() {
output_vector.push("--default-macro-constant-type".into());
output_vector
.push(self.options.default_macro_constant_type.as_str().into());
}
if self.options.default_alias_style != Default::default() {
output_vector.push("--default-alias-style".into());
output_vector
.push(self.options.default_alias_style.as_str().into());
}
if self.options.default_non_copy_union_style != Default::default() {
output_vector.push("--default-non-copy-union-style".into());
output_vector.push(
self.options.default_non_copy_union_style.as_str().into(),
);
}
let regex_sets = &[
(&self.options.bitfield_enums, "--bitfield-enum"),
(&self.options.newtype_enums, "--newtype-enum"),
(&self.options.newtype_global_enums, "--newtype-global-enum"),
(&self.options.rustified_enums, "--rustified-enum"),
(
&self.options.rustified_non_exhaustive_enums,
"--rustified-enum-non-exhaustive",
),
(
&self.options.constified_enum_modules,
"--constified-enum-module",
),
(&self.options.constified_enums, "--constified-enum"),
(&self.options.type_alias, "--type-alias"),
(&self.options.new_type_alias, "--new-type-alias"),
(&self.options.new_type_alias_deref, "--new-type-alias-deref"),
(
&self.options.bindgen_wrapper_union,
"--bindgen-wrapper-union",
),
(&self.options.manually_drop_union, "--manually-drop-union"),
(&self.options.blocklisted_types, "--blocklist-type"),
(&self.options.blocklisted_functions, "--blocklist-function"),
(&self.options.blocklisted_items, "--blocklist-item"),
(&self.options.blocklisted_files, "--blocklist-file"),
(&self.options.opaque_types, "--opaque-type"),
(&self.options.allowlisted_functions, "--allowlist-function"),
(&self.options.allowlisted_types, "--allowlist-type"),
(&self.options.allowlisted_vars, "--allowlist-var"),
(&self.options.allowlisted_files, "--allowlist-file"),
(&self.options.no_partialeq_types, "--no-partialeq"),
(&self.options.no_copy_types, "--no-copy"),
(&self.options.no_debug_types, "--no-debug"),
(&self.options.no_default_types, "--no-default"),
(&self.options.no_hash_types, "--no-hash"),
(&self.options.must_use_types, "--must-use-type"),
];
for (set, flag) in regex_sets {
for item in set.get_items() {
output_vector.push((*flag).to_owned());
output_vector.push(item.to_owned());
}
}
for (abi, set) in &self.options.abi_overrides {
for item in set.get_items() {
output_vector.push("--override-abi".to_owned());
output_vector.push(format!("{}={}", item, abi));
}
}
if !self.options.layout_tests {
output_vector.push("--no-layout-tests".into());
}
if self.options.impl_debug {
output_vector.push("--impl-debug".into());
}
if self.options.impl_partialeq {
output_vector.push("--impl-partialeq".into());
}
if !self.options.derive_copy {
output_vector.push("--no-derive-copy".into());
}
if !self.options.derive_debug {
output_vector.push("--no-derive-debug".into());
}
if !self.options.derive_default {
output_vector.push("--no-derive-default".into());
} else {
output_vector.push("--with-derive-default".into());
}
if self.options.derive_hash {
output_vector.push("--with-derive-hash".into());
}
if self.options.derive_partialord {
output_vector.push("--with-derive-partialord".into());
}
if self.options.derive_ord {
output_vector.push("--with-derive-ord".into());
}
if self.options.derive_partialeq {
output_vector.push("--with-derive-partialeq".into());
}
if self.options.derive_eq {
output_vector.push("--with-derive-eq".into());
}
if self.options.time_phases {
output_vector.push("--time-phases".into());
}
if !self.options.generate_comments {
output_vector.push("--no-doc-comments".into());
}
if !self.options.allowlist_recursively {
output_vector.push("--no-recursive-allowlist".into());
}
if self.options.objc_extern_crate {
output_vector.push("--objc-extern-crate".into());
}
if self.options.generate_block {
output_vector.push("--generate-block".into());
}
if self.options.block_extern_crate {
output_vector.push("--block-extern-crate".into());
}
if self.options.builtins {
output_vector.push("--builtins".into());
}
if let Some(ref prefix) = self.options.ctypes_prefix {
output_vector.push("--ctypes-prefix".into());
output_vector.push(prefix.clone());
}
if self.options.anon_fields_prefix != DEFAULT_ANON_FIELDS_PREFIX {
output_vector.push("--anon-fields-prefix".into());
output_vector.push(self.options.anon_fields_prefix.clone());
}
if self.options.emit_ast {
output_vector.push("--emit-clang-ast".into());
}
if self.options.emit_ir {
output_vector.push("--emit-ir".into());
}
if let Some(ref graph) = self.options.emit_ir_graphviz {
output_vector.push("--emit-ir-graphviz".into());
output_vector.push(graph.clone())
}
if self.options.enable_cxx_namespaces {
output_vector.push("--enable-cxx-namespaces".into());
}
if self.options.enable_function_attribute_detection {
output_vector.push("--enable-function-attribute-detection".into());
}
if self.options.disable_name_namespacing {
output_vector.push("--disable-name-namespacing".into());
}
if self.options.disable_nested_struct_naming {
output_vector.push("--disable-nested-struct-naming".into());
}
if self.options.disable_header_comment {
output_vector.push("--disable-header-comment".into());
}
if !self.options.codegen_config.functions() {
output_vector.push("--ignore-functions".into());
}
output_vector.push("--generate".into());
let mut options: Vec<String> = Vec::new();
if self.options.codegen_config.functions() {
options.push("functions".into());
}
if self.options.codegen_config.types() {
options.push("types".into());
}
if self.options.codegen_config.vars() {
options.push("vars".into());
}
if self.options.codegen_config.methods() {
options.push("methods".into());
}
if self.options.codegen_config.constructors() {
options.push("constructors".into());
}
if self.options.codegen_config.destructors() {
options.push("destructors".into());
}
output_vector.push(options.join(","));
if !self.options.codegen_config.methods() {
output_vector.push("--ignore-methods".into());
}
if !self.options.convert_floats {
output_vector.push("--no-convert-floats".into());
}
if !self.options.prepend_enum_name {
output_vector.push("--no-prepend-enum-name".into());
}
if self.options.fit_macro_constants {
output_vector.push("--fit-macro-constant-types".into());
}
if self.options.array_pointers_in_arguments {
output_vector.push("--use-array-pointers-in-arguments".into());
}
if let Some(ref wasm_import_module_name) =
self.options.wasm_import_module_name
{
output_vector.push("--wasm-import-module-name".into());
output_vector.push(wasm_import_module_name.clone());
}
for line in &self.options.raw_lines {
output_vector.push("--raw-line".into());
output_vector.push(line.clone());
}
for (module, lines) in &self.options.module_lines {
for line in lines.iter() {
output_vector.push("--module-raw-line".into());
output_vector.push(module.clone());
output_vector.push(line.clone());
}
}
if self.options.use_core {
output_vector.push("--use-core".into());
}
if self.options.conservative_inline_namespaces {
output_vector.push("--conservative-inline-namespaces".into());
}
if self.options.generate_inline_functions {
output_vector.push("--generate-inline-functions".into());
}
if !self.options.record_matches {
output_vector.push("--no-record-matches".into());
}
if !self.options.size_t_is_usize {
output_vector.push("--no-size_t-is-usize".into());
}
if !self.options.rustfmt_bindings {
output_vector.push("--no-rustfmt-bindings".into());
}
if let Some(path) = self
.options
.rustfmt_configuration_file
.as_ref()
.and_then(|f| f.to_str())
{
output_vector.push("--rustfmt-configuration-file".into());
output_vector.push(path.into());
}
if let Some(ref name) = self.options.dynamic_library_name {
output_vector.push("--dynamic-loading".into());
output_vector.push(name.clone());
}
if self.options.dynamic_link_require_all {
output_vector.push("--dynamic-link-require-all".into());
}
if self.options.respect_cxx_access_specs {
output_vector.push("--respect-cxx-access-specs".into());
}
if self.options.translate_enum_integer_types {
output_vector.push("--translate-enum-integer-types".into());
}
if self.options.c_naming {
output_vector.push("--c-naming".into());
}
if self.options.force_explicit_padding {
output_vector.push("--explicit-padding".into());
}
if self.options.vtable_generation {
output_vector.push("--vtable-generation".into());
}
if self.options.sort_semantically {
output_vector.push("--sort-semantically".into());
}
if self.options.merge_extern_blocks {
output_vector.push("--merge-extern-blocks".into());
}
if self.options.wrap_unsafe_ops {
output_vector.push("--wrap-unsafe-ops".into());
}
#[cfg(feature = "cli")]
for callbacks in &self.options.parse_callbacks {
output_vector.extend(callbacks.cli_args());
}
if self.options.wrap_static_fns {
output_vector.push("--wrap-static-fns".into())
}
if let Some(ref path) = self.options.wrap_static_fns_path {
output_vector.push("--wrap-static-fns-path".into());
output_vector.push(path.display().to_string());
}
if let Some(ref suffix) = self.options.wrap_static_fns_suffix {
output_vector.push("--wrap-static-fns-suffix".into());
output_vector.push(suffix.clone());
}
if cfg!(feature = "experimental") {
output_vector.push("--experimental".into());
}
output_vector.push("--".into());
if !self.options.clang_args.is_empty() {
output_vector.extend(self.options.clang_args.iter().cloned());
}
for header in &self.options.input_headers
[..self.options.input_headers.len().saturating_sub(1)]
{
output_vector.push("-include".to_string());
output_vector.push(header.clone());
}
output_vector
}
pub fn header<T: Into<String>>(mut self, header: T) -> Builder {
self.options.input_headers.push(header.into());
self
}
pub fn depfile<H: Into<String>, D: Into<PathBuf>>(
mut self,
output_module: H,
depfile: D,
) -> Builder {
self.options.depfile = Some(deps::DepfileSpec {
output_module: output_module.into(),
depfile_path: depfile.into(),
});
self
}
pub fn header_contents(mut self, name: &str, contents: &str) -> Builder {
let absolute_path = env::current_dir()
.expect("Cannot retrieve current directory")
.join(name)
.to_str()
.expect("Cannot convert current directory name to string")
.to_owned();
self.options
.input_header_contents
.push((absolute_path, contents.into()));
self
}
pub fn rust_target(mut self, rust_target: RustTarget) -> Self {
#[allow(deprecated)]
if rust_target <= RustTarget::Stable_1_30 {
warn!(
"The {} rust target is deprecated. If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues",
String::from(rust_target)
);
}
self.options.set_rust_target(rust_target);
self
}
pub fn disable_untagged_union(mut self) -> Self {
self.options.rust_features.untagged_union = false;
self
}
pub fn disable_header_comment(mut self) -> Self {
self.options.disable_header_comment = true;
self
}
pub fn emit_ir_graphviz<T: Into<String>>(mut self, path: T) -> Builder {
let path = path.into();
self.options.emit_ir_graphviz = Some(path);
self
}
pub fn generate_comments(mut self, doit: bool) -> Self {
self.options.generate_comments = doit;
self
}
pub fn allowlist_recursively(mut self, doit: bool) -> Self {
self.options.allowlist_recursively = doit;
self
}
pub fn objc_extern_crate(mut self, doit: bool) -> Self {
self.options.objc_extern_crate = doit;
self
}
pub fn generate_block(mut self, doit: bool) -> Self {
self.options.generate_block = doit;
self
}
pub fn block_extern_crate(mut self, doit: bool) -> Self {
self.options.block_extern_crate = doit;
self
}
pub fn trust_clang_mangling(mut self, doit: bool) -> Self {
self.options.enable_mangling = doit;
self
}
fn_with_regex_arg! {
pub fn blocklist_type<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.blocklisted_types.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn blocklist_function<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.blocklisted_functions.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn blocklist_item<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.blocklisted_items.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn blocklist_file<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.blocklisted_files.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn opaque_type<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.opaque_types.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn allowlist_type<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.allowlisted_types.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn allowlist_function<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.allowlisted_functions.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn allowlist_var<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.allowlisted_vars.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn allowlist_file<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.allowlisted_files.insert(arg);
self
}
}
pub fn default_enum_style(
mut self,
arg: codegen::EnumVariation,
) -> Builder {
self.options.default_enum_style = arg;
self
}
fn_with_regex_arg! {
pub fn bitfield_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.bitfield_enums.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn newtype_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.newtype_enums.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn newtype_global_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.newtype_global_enums.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn rustified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.rustified_enums.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn rustified_non_exhaustive_enum<T: AsRef<str>>(
mut self,
arg: T,
) -> Builder {
self.options.rustified_non_exhaustive_enums.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn constified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.constified_enums.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn constified_enum_module<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.constified_enum_modules.insert(arg);
self
}
}
pub fn default_macro_constant_type(
mut self,
arg: codegen::MacroTypeVariation,
) -> Builder {
self.options.default_macro_constant_type = arg;
self
}
pub fn default_alias_style(
mut self,
arg: codegen::AliasVariation,
) -> Builder {
self.options.default_alias_style = arg;
self
}
fn_with_regex_arg! {
pub fn type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.type_alias.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn new_type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.new_type_alias.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn new_type_alias_deref<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.new_type_alias_deref.insert(arg);
self
}
}
pub fn default_non_copy_union_style(
mut self,
arg: codegen::NonCopyUnionStyle,
) -> Self {
self.options.default_non_copy_union_style = arg;
self
}
fn_with_regex_arg! {
pub fn bindgen_wrapper_union<T: AsRef<str>>(mut self, arg: T) -> Self {
self.options.bindgen_wrapper_union.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn manually_drop_union<T: AsRef<str>>(mut self, arg: T) -> Self {
self.options.manually_drop_union.insert(arg);
self
}
}
fn_with_regex_arg! {
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Self {
self.options.raw_lines.push(arg.into());
self
}
}
pub fn module_raw_line<T, U>(mut self, mod_: T, line: U) -> Self
where
T: Into<String>,
U: Into<String>,
{
self.options
.module_lines
.entry(mod_.into())
.or_insert_with(Vec::new)
.push(line.into());
self
}
pub fn module_raw_lines<T, I>(mut self, mod_: T, lines: I) -> Self
where
T: Into<String>,
I: IntoIterator,
I::Item: Into<String>,
{
self.options
.module_lines
.entry(mod_.into())
.or_insert_with(Vec::new)
.extend(lines.into_iter().map(Into::into));
self
}
pub fn clang_arg<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.clang_args.push(arg.into());
self
}
pub fn clang_args<I>(mut self, iter: I) -> Builder
where
I: IntoIterator,
I::Item: AsRef<str>,
{
for arg in iter {
self = self.clang_arg(arg.as_ref())
}
self
}
pub fn emit_builtins(mut self) -> Builder {
self.options.builtins = true;
self
}
pub fn no_convert_floats(mut self) -> Self {
self.options.convert_floats = false;
self
}
pub fn layout_tests(mut self, doit: bool) -> Self {
self.options.layout_tests = doit;
self
}
pub fn impl_debug(mut self, doit: bool) -> Self {
self.options.impl_debug = doit;
self
}
pub fn impl_partialeq(mut self, doit: bool) -> Self {
self.options.impl_partialeq = doit;
self
}
pub fn derive_copy(mut self, doit: bool) -> Self {
self.options.derive_copy = doit;
self
}
pub fn derive_debug(mut self, doit: bool) -> Self {
self.options.derive_debug = doit;
self
}
pub fn derive_default(mut self, doit: bool) -> Self {
self.options.derive_default = doit;
self
}
pub fn derive_hash(mut self, doit: bool) -> Self {
self.options.derive_hash = doit;
self
}
pub fn derive_partialord(mut self, doit: bool) -> Self {
self.options.derive_partialord = doit;
if !doit {
self.options.derive_ord = false;
}
self
}
pub fn derive_ord(mut self, doit: bool) -> Self {
self.options.derive_ord = doit;
self.options.derive_partialord = doit;
self
}
pub fn derive_partialeq(mut self, doit: bool) -> Self {
self.options.derive_partialeq = doit;
if !doit {
self.options.derive_eq = false;
}
self
}
pub fn derive_eq(mut self, doit: bool) -> Self {
self.options.derive_eq = doit;
if doit {
self.options.derive_partialeq = doit;
}
self
}
pub fn time_phases(mut self, doit: bool) -> Self {
self.options.time_phases = doit;
self
}
pub fn emit_clang_ast(mut self) -> Builder {
self.options.emit_ast = true;
self
}
pub fn emit_ir(mut self) -> Builder {
self.options.emit_ir = true;
self
}
pub fn enable_cxx_namespaces(mut self) -> Builder {
self.options.enable_cxx_namespaces = true;
self
}
pub fn enable_function_attribute_detection(mut self) -> Self {
self.options.enable_function_attribute_detection = true;
self
}
pub fn disable_name_namespacing(mut self) -> Builder {
self.options.disable_name_namespacing = true;
self
}
pub fn disable_nested_struct_naming(mut self) -> Builder {
self.options.disable_nested_struct_naming = true;
self
}
pub fn conservative_inline_namespaces(mut self) -> Builder {
self.options.conservative_inline_namespaces = true;
self
}
pub fn generate_inline_functions(mut self, doit: bool) -> Self {
self.options.generate_inline_functions = doit;
self
}
pub fn ignore_functions(mut self) -> Builder {
self.options.codegen_config.remove(CodegenConfig::FUNCTIONS);
self
}
pub fn ignore_methods(mut self) -> Builder {
self.options.codegen_config.remove(CodegenConfig::METHODS);
self
}
pub fn use_core(mut self) -> Builder {
self.options.use_core = true;
self
}
pub fn ctypes_prefix<T: Into<String>>(mut self, prefix: T) -> Builder {
self.options.ctypes_prefix = Some(prefix.into());
self
}
pub fn anon_fields_prefix<T: Into<String>>(mut self, prefix: T) -> Builder {
self.options.anon_fields_prefix = prefix.into();
self
}
pub fn parse_callbacks(
mut self,
cb: Box<dyn callbacks::ParseCallbacks>,
) -> Self {
self.options.parse_callbacks.push(Rc::from(cb));
self
}
pub fn with_codegen_config(mut self, config: CodegenConfig) -> Self {
self.options.codegen_config = config;
self
}
pub fn detect_include_paths(mut self, doit: bool) -> Self {
self.options.detect_include_paths = doit;
self
}
pub fn fit_macro_constants(mut self, doit: bool) -> Self {
self.options.fit_macro_constants = doit;
self
}
pub fn prepend_enum_name(mut self, doit: bool) -> Self {
self.options.prepend_enum_name = doit;
self
}
pub fn size_t_is_usize(mut self, is: bool) -> Self {
self.options.size_t_is_usize = is;
self
}
pub fn rustfmt_bindings(mut self, doit: bool) -> Self {
self.options.rustfmt_bindings = doit;
self
}
pub fn record_matches(mut self, doit: bool) -> Self {
self.options.record_matches = doit;
self
}
pub fn rustfmt_configuration_file(mut self, path: Option<PathBuf>) -> Self {
self = self.rustfmt_bindings(true);
self.options.rustfmt_configuration_file = path;
self
}
pub fn with_rustfmt<P: Into<PathBuf>>(mut self, path: P) -> Self {
self.options.rustfmt_path = Some(path.into());
self
}
pub fn explicit_padding(mut self, doit: bool) -> Self {
self.options.force_explicit_padding = doit;
self
}
pub fn vtable_generation(mut self, doit: bool) -> Self {
self.options.vtable_generation = doit;
self
}
pub fn sort_semantically(mut self, doit: bool) -> Self {
self.options.sort_semantically = doit;
self
}
pub fn merge_extern_blocks(mut self, doit: bool) -> Self {
self.options.merge_extern_blocks = doit;
self
}
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
self.options.clang_args.extend(get_extra_clang_args());
self.options.clang_args.extend(
self.options.input_headers
[..self.options.input_headers.len().saturating_sub(1)]
.iter()
.flat_map(|header| ["-include".into(), header.to_string()]),
);
let input_unsaved_files =
std::mem::take(&mut self.options.input_header_contents)
.into_iter()
.map(|(name, contents)| clang::UnsavedFile::new(name, contents))
.collect::<Vec<_>>();
Bindings::generate(self.options, input_unsaved_files)
}
pub fn dump_preprocessed_input(&self) -> io::Result<()> {
let clang =
clang_sys::support::Clang::find(None, &[]).ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"Cannot find clang executable",
)
})?;
let mut wrapper_contents = String::new();
let mut is_cpp = args_are_cpp(&self.options.clang_args);
for header in &self.options.input_headers {
is_cpp |= file_is_cpp(header);
wrapper_contents.push_str("#include \"");
wrapper_contents.push_str(header);
wrapper_contents.push_str("\"\n");
}
for (name, contents) in &self.options.input_header_contents {
is_cpp |= file_is_cpp(name);
wrapper_contents.push_str("#line 0 \"");
wrapper_contents.push_str(name);
wrapper_contents.push_str("\"\n");
wrapper_contents.push_str(contents);
}
let wrapper_path = PathBuf::from(if is_cpp {
"__bindgen.cpp"
} else {
"__bindgen.c"
});
{
let mut wrapper_file = File::create(&wrapper_path)?;
wrapper_file.write_all(wrapper_contents.as_bytes())?;
}
let mut cmd = Command::new(clang.path);
cmd.arg("-save-temps")
.arg("-E")
.arg("-C")
.arg("-c")
.arg(&wrapper_path)
.stdout(Stdio::piped());
for a in &self.options.clang_args {
cmd.arg(a);
}
for a in get_extra_clang_args() {
cmd.arg(a);
}
let mut child = cmd.spawn()?;
let mut preprocessed = child.stdout.take().unwrap();
let mut file = File::create(if is_cpp {
"__bindgen.ii"
} else {
"__bindgen.i"
})?;
io::copy(&mut preprocessed, &mut file)?;
if child.wait()?.success() {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"clang exited with non-zero status",
))
}
}
fn_with_regex_arg! {
pub fn no_partialeq<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.no_partialeq_types.insert(arg.into());
self
}
}
fn_with_regex_arg! {
pub fn no_copy<T: Into<String>>(mut self, arg: T) -> Self {
self.options.no_copy_types.insert(arg.into());
self
}
}
fn_with_regex_arg! {
pub fn no_debug<T: Into<String>>(mut self, arg: T) -> Self {
self.options.no_debug_types.insert(arg.into());
self
}
}
fn_with_regex_arg! {
pub fn no_default<T: Into<String>>(mut self, arg: T) -> Self {
self.options.no_default_types.insert(arg.into());
self
}
}
fn_with_regex_arg! {
pub fn no_hash<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.no_hash_types.insert(arg.into());
self
}
}
fn_with_regex_arg! {
pub fn must_use_type<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.must_use_types.insert(arg.into());
self
}
}
pub fn array_pointers_in_arguments(mut self, doit: bool) -> Self {
self.options.array_pointers_in_arguments = doit;
self
}
pub fn wasm_import_module_name<T: Into<String>>(
mut self,
import_name: T,
) -> Self {
self.options.wasm_import_module_name = Some(import_name.into());
self
}
pub fn dynamic_library_name<T: Into<String>>(
mut self,
dynamic_library_name: T,
) -> Self {
self.options.dynamic_library_name = Some(dynamic_library_name.into());
self
}
pub fn dynamic_link_require_all(mut self, req: bool) -> Self {
self.options.dynamic_link_require_all = req;
self
}
pub fn respect_cxx_access_specs(mut self, doit: bool) -> Self {
self.options.respect_cxx_access_specs = doit;
self
}
pub fn translate_enum_integer_types(mut self, doit: bool) -> Self {
self.options.translate_enum_integer_types = doit;
self
}
pub fn c_naming(mut self, doit: bool) -> Self {
self.options.c_naming = doit;
self
}
pub fn override_abi<T: Into<String>>(mut self, abi: Abi, arg: T) -> Self {
self.options
.abi_overrides
.entry(abi)
.or_default()
.insert(arg.into());
self
}
pub fn wrap_unsafe_ops(mut self, doit: bool) -> Self {
self.options.wrap_unsafe_ops = doit;
self
}
#[cfg(feature = "experimental")]
pub fn wrap_static_fns(mut self, doit: bool) -> Self {
self.options.wrap_static_fns = doit;
self
}
#[cfg(feature = "experimental")]
pub fn wrap_static_fns_path<T: AsRef<Path>>(mut self, path: T) -> Self {
self.options.wrap_static_fns_path = Some(path.as_ref().to_owned());
self
}
#[cfg(feature = "experimental")]
pub fn wrap_static_fns_suffix<T: AsRef<str>>(mut self, suffix: T) -> Self {
self.options.wrap_static_fns_suffix = Some(suffix.as_ref().to_owned());
self
}
}
#[derive(Clone, Debug)]
struct BindgenOptions {
blocklisted_types: RegexSet,
blocklisted_functions: RegexSet,
blocklisted_items: RegexSet,
blocklisted_files: RegexSet,
opaque_types: RegexSet,
rustfmt_path: Option<PathBuf>,
depfile: Option<deps::DepfileSpec>,
allowlisted_types: RegexSet,
allowlisted_functions: RegexSet,
allowlisted_vars: RegexSet,
allowlisted_files: RegexSet,
default_enum_style: codegen::EnumVariation,
bitfield_enums: RegexSet,
newtype_enums: RegexSet,
newtype_global_enums: RegexSet,
rustified_enums: RegexSet,
rustified_non_exhaustive_enums: RegexSet,
constified_enum_modules: RegexSet,
constified_enums: RegexSet,
default_macro_constant_type: codegen::MacroTypeVariation,
default_alias_style: codegen::AliasVariation,
type_alias: RegexSet,
new_type_alias: RegexSet,
new_type_alias_deref: RegexSet,
default_non_copy_union_style: codegen::NonCopyUnionStyle,
bindgen_wrapper_union: RegexSet,
manually_drop_union: RegexSet,
builtins: bool,
emit_ast: bool,
emit_ir: bool,
emit_ir_graphviz: Option<String>,
enable_cxx_namespaces: bool,
enable_function_attribute_detection: bool,
disable_name_namespacing: bool,
disable_nested_struct_naming: bool,
disable_header_comment: bool,
layout_tests: bool,
impl_debug: bool,
impl_partialeq: bool,
derive_copy: bool,
derive_debug: bool,
derive_default: bool,
derive_hash: bool,
derive_partialord: bool,
derive_ord: bool,
derive_partialeq: bool,
derive_eq: bool,
use_core: bool,
ctypes_prefix: Option<String>,
anon_fields_prefix: String,
time_phases: bool,
convert_floats: bool,
raw_lines: Vec<String>,
module_lines: HashMap<String, Vec<String>>,
clang_args: Vec<String>,
input_headers: Vec<String>,
input_header_contents: Vec<(String, String)>,
parse_callbacks: Vec<Rc<dyn callbacks::ParseCallbacks>>,
codegen_config: CodegenConfig,
conservative_inline_namespaces: bool,
generate_comments: bool,
generate_inline_functions: bool,
allowlist_recursively: bool,
objc_extern_crate: bool,
generate_block: bool,
block_extern_crate: bool,
enable_mangling: bool,
detect_include_paths: bool,
fit_macro_constants: bool,
prepend_enum_name: bool,
rust_target: RustTarget,
rust_features: RustFeatures,
record_matches: bool,
size_t_is_usize: bool,
rustfmt_bindings: bool,
rustfmt_configuration_file: Option<PathBuf>,
no_partialeq_types: RegexSet,
no_copy_types: RegexSet,
no_debug_types: RegexSet,
no_default_types: RegexSet,
no_hash_types: RegexSet,
must_use_types: RegexSet,
array_pointers_in_arguments: bool,
wasm_import_module_name: Option<String>,
dynamic_library_name: Option<String>,
dynamic_link_require_all: bool,
respect_cxx_access_specs: bool,
translate_enum_integer_types: bool,
c_naming: bool,
force_explicit_padding: bool,
vtable_generation: bool,
sort_semantically: bool,
merge_extern_blocks: bool,
abi_overrides: HashMap<Abi, RegexSet>,
wrap_unsafe_ops: bool,
wrap_static_fns: bool,
wrap_static_fns_suffix: Option<String>,
wrap_static_fns_path: Option<PathBuf>,
}
impl BindgenOptions {
fn build(&mut self) {
let regex_sets = [
&mut self.allowlisted_vars,
&mut self.allowlisted_types,
&mut self.allowlisted_functions,
&mut self.allowlisted_files,
&mut self.blocklisted_types,
&mut self.blocklisted_functions,
&mut self.blocklisted_items,
&mut self.blocklisted_files,
&mut self.opaque_types,
&mut self.bitfield_enums,
&mut self.constified_enums,
&mut self.constified_enum_modules,
&mut self.newtype_enums,
&mut self.newtype_global_enums,
&mut self.rustified_enums,
&mut self.rustified_non_exhaustive_enums,
&mut self.type_alias,
&mut self.new_type_alias,
&mut self.new_type_alias_deref,
&mut self.bindgen_wrapper_union,
&mut self.manually_drop_union,
&mut self.no_partialeq_types,
&mut self.no_copy_types,
&mut self.no_debug_types,
&mut self.no_default_types,
&mut self.no_hash_types,
&mut self.must_use_types,
];
let record_matches = self.record_matches;
for regex_set in self.abi_overrides.values_mut().chain(regex_sets) {
regex_set.build(record_matches);
}
}
pub fn set_rust_target(&mut self, rust_target: RustTarget) {
self.rust_target = rust_target;
self.rust_features = rust_target.into();
}
pub fn rust_features(&self) -> RustFeatures {
self.rust_features
}
fn last_callback<T>(
&self,
f: impl Fn(&dyn callbacks::ParseCallbacks) -> Option<T>,
) -> Option<T> {
self.parse_callbacks
.iter()
.filter_map(|cb| f(cb.as_ref()))
.last()
}
fn all_callbacks<T>(
&self,
f: impl Fn(&dyn callbacks::ParseCallbacks) -> Vec<T>,
) -> Vec<T> {
self.parse_callbacks
.iter()
.flat_map(|cb| f(cb.as_ref()))
.collect()
}
fn process_comment(&self, comment: &str) -> String {
let comment = comment::preprocess(comment);
self.parse_callbacks
.last()
.and_then(|cb| cb.process_comment(&comment))
.unwrap_or(comment)
}
}
impl Default for BindgenOptions {
fn default() -> BindgenOptions {
macro_rules! options {
($($field:ident $(: $value:expr)?,)* --default-fields-- $($default_field:ident,)*) => {
BindgenOptions {
$($field $(: $value)*,)*
$($default_field: Default::default(),)*
}
};
}
let rust_target = RustTarget::default();
options! {
rust_target,
rust_features: rust_target.into(),
layout_tests: true,
derive_copy: true,
derive_debug: true,
anon_fields_prefix: DEFAULT_ANON_FIELDS_PREFIX.into(),
convert_floats: true,
codegen_config: CodegenConfig::all(),
generate_comments: true,
allowlist_recursively: true,
enable_mangling: true,
detect_include_paths: true,
prepend_enum_name: true,
record_matches: true,
rustfmt_bindings: true,
size_t_is_usize: true,
--default-fields--
blocklisted_types,
blocklisted_functions,
blocklisted_items,
blocklisted_files,
opaque_types,
rustfmt_path,
depfile,
allowlisted_types,
allowlisted_functions,
allowlisted_vars,
allowlisted_files,
default_enum_style,
bitfield_enums,
newtype_enums,
newtype_global_enums,
rustified_enums,
rustified_non_exhaustive_enums,
constified_enums,
constified_enum_modules,
default_macro_constant_type,
default_alias_style,
type_alias,
new_type_alias,
new_type_alias_deref,
default_non_copy_union_style,
bindgen_wrapper_union,
manually_drop_union,
builtins,
emit_ast,
emit_ir,
emit_ir_graphviz,
impl_debug,
impl_partialeq,
derive_default,
derive_hash,
derive_partialord,
derive_ord,
derive_partialeq,
derive_eq,
enable_cxx_namespaces,
enable_function_attribute_detection,
disable_name_namespacing,
disable_nested_struct_naming,
disable_header_comment,
use_core,
ctypes_prefix,
raw_lines,
module_lines,
clang_args,
input_headers,
input_header_contents,
parse_callbacks,
conservative_inline_namespaces,
generate_inline_functions,
generate_block,
objc_extern_crate,
block_extern_crate,
fit_macro_constants,
time_phases,
rustfmt_configuration_file,
no_partialeq_types,
no_copy_types,
no_debug_types,
no_default_types,
no_hash_types,
must_use_types,
array_pointers_in_arguments,
wasm_import_module_name,
dynamic_library_name,
dynamic_link_require_all,
respect_cxx_access_specs,
translate_enum_integer_types,
c_naming,
force_explicit_padding,
vtable_generation,
sort_semantically,
merge_extern_blocks,
abi_overrides,
wrap_unsafe_ops,
wrap_static_fns,
wrap_static_fns_suffix,
wrap_static_fns_path,
}
}
}
#[cfg(feature = "runtime")]
fn ensure_libclang_is_loaded() {
if clang_sys::is_loaded() {
return;
}
lazy_static! {
static ref LIBCLANG: std::sync::Arc<clang_sys::SharedLibrary> = {
clang_sys::load().expect("Unable to find libclang");
clang_sys::get_library().expect(
"We just loaded libclang and it had better still be \
here!",
)
};
}
clang_sys::set_library(Some(LIBCLANG.clone()));
}
#[cfg(not(feature = "runtime"))]
fn ensure_libclang_is_loaded() {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BindgenError {
FolderAsHeader(PathBuf),
InsufficientPermissions(PathBuf),
NotExist(PathBuf),
ClangDiagnostic(String),
Codegen(CodegenError),
}
impl std::fmt::Display for BindgenError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BindgenError::FolderAsHeader(h) => {
write!(f, "'{}' is a folder", h.display())
}
BindgenError::InsufficientPermissions(h) => {
write!(f, "insufficient permissions to read '{}'", h.display())
}
BindgenError::NotExist(h) => {
write!(f, "header '{}' does not exist.", h.display())
}
BindgenError::ClangDiagnostic(message) => {
write!(f, "clang diagnosed error: {}", message)
}
BindgenError::Codegen(err) => {
write!(f, "codegen error: {}", err)
}
}
}
}
impl std::error::Error for BindgenError {}
#[derive(Debug)]
pub struct Bindings {
options: BindgenOptions,
warnings: Vec<String>,
module: proc_macro2::TokenStream,
}
pub(crate) const HOST_TARGET: &str =
include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
fn rust_to_clang_target(rust_target: &str) -> String {
if rust_target.starts_with("aarch64-apple-") {
let mut clang_target = "arm64-apple-".to_owned();
clang_target
.push_str(rust_target.strip_prefix("aarch64-apple-").unwrap());
return clang_target;
} else if rust_target.starts_with("riscv64gc-") {
let mut clang_target = "riscv64-".to_owned();
clang_target.push_str(rust_target.strip_prefix("riscv64gc-").unwrap());
return clang_target;
} else if rust_target.ends_with("-espidf") {
let mut clang_target =
rust_target.strip_suffix("-espidf").unwrap().to_owned();
clang_target.push_str("-elf");
if clang_target.starts_with("riscv32imc-") {
clang_target = "riscv32-".to_owned() +
clang_target.strip_prefix("riscv32imc-").unwrap();
}
return clang_target;
}
rust_target.to_owned()
}
fn find_effective_target(clang_args: &[String]) -> (String, bool) {
let mut args = clang_args.iter();
while let Some(opt) = args.next() {
if opt.starts_with("--target=") {
let mut split = opt.split('=');
split.next();
return (split.next().unwrap().to_owned(), true);
}
if opt == "-target" {
if let Some(target) = args.next() {
return (target.clone(), true);
}
}
}
if let Ok(t) = env::var("TARGET") {
return (rust_to_clang_target(&t), false);
}
(rust_to_clang_target(HOST_TARGET), false)
}
impl Bindings {
pub(crate) fn generate(
mut options: BindgenOptions,
input_unsaved_files: Vec<clang::UnsavedFile>,
) -> Result<Bindings, BindgenError> {
ensure_libclang_is_loaded();
#[cfg(feature = "runtime")]
debug!(
"Generating bindings, libclang at {}",
clang_sys::get_library().unwrap().path().display()
);
#[cfg(not(feature = "runtime"))]
debug!("Generating bindings, libclang linked");
options.build();
let (effective_target, explicit_target) =
find_effective_target(&options.clang_args);
let is_host_build =
rust_to_clang_target(HOST_TARGET) == effective_target;
if !explicit_target && !is_host_build {
options
.clang_args
.insert(0, format!("--target={}", effective_target));
};
fn detect_include_paths(options: &mut BindgenOptions) {
if !options.detect_include_paths {
return;
}
let clang_args_for_clang_sys = {
let mut last_was_include_prefix = false;
options
.clang_args
.iter()
.filter(|arg| {
if last_was_include_prefix {
last_was_include_prefix = false;
return false;
}
let arg = &**arg;
if arg == "-I" || arg == "--include-directory" {
last_was_include_prefix = true;
return false;
}
if arg.starts_with("-I") ||
arg.starts_with("--include-directory=")
{
return false;
}
true
})
.cloned()
.collect::<Vec<_>>()
};
debug!(
"Trying to find clang with flags: {:?}",
clang_args_for_clang_sys
);
let clang = match clang_sys::support::Clang::find(
None,
&clang_args_for_clang_sys,
) {
None => return,
Some(clang) => clang,
};
debug!("Found clang: {:?}", clang);
let is_cpp = args_are_cpp(&options.clang_args) ||
options.input_headers.iter().any(|h| file_is_cpp(h));
let search_paths = if is_cpp {
clang.cpp_search_paths
} else {
clang.c_search_paths
};
if let Some(search_paths) = search_paths {
for path in search_paths.into_iter() {
if let Ok(path) = path.into_os_string().into_string() {
options.clang_args.push("-isystem".to_owned());
options.clang_args.push(path);
}
}
}
}
detect_include_paths(&mut options);
#[cfg(unix)]
fn can_read(perms: &std::fs::Permissions) -> bool {
use std::os::unix::fs::PermissionsExt;
perms.mode() & 0o444 > 0
}
#[cfg(not(unix))]
fn can_read(_: &std::fs::Permissions) -> bool {
true
}
if let Some(h) = options.input_headers.last() {
let path = Path::new(h);
if let Ok(md) = std::fs::metadata(path) {
if md.is_dir() {
return Err(BindgenError::FolderAsHeader(path.into()));
}
if !can_read(&md.permissions()) {
return Err(BindgenError::InsufficientPermissions(
path.into(),
));
}
let h = h.clone();
options.clang_args.push(h);
} else {
return Err(BindgenError::NotExist(path.into()));
}
}
for (idx, f) in input_unsaved_files.iter().enumerate() {
if idx != 0 || !options.input_headers.is_empty() {
options.clang_args.push("-include".to_owned());
}
options.clang_args.push(f.name.to_str().unwrap().to_owned())
}
debug!("Fixed-up options: {:?}", options);
let time_phases = options.time_phases;
let mut context = BindgenContext::new(options, &input_unsaved_files);
if is_host_build {
debug_assert_eq!(
context.target_pointer_size(),
std::mem::size_of::<*mut ()>(),
"{:?} {:?}",
effective_target,
HOST_TARGET
);
}
{
let _t = time::Timer::new("parse").with_output(time_phases);
parse(&mut context)?;
}
let (module, options, warnings) =
codegen::codegen(context).map_err(BindgenError::Codegen)?;
Ok(Bindings {
options,
warnings,
module,
})
}
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(path.as_ref())?;
self.write(Box::new(file))?;
Ok(())
}
pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> {
if !self.options.disable_header_comment {
let version = option_env!("CARGO_PKG_VERSION");
let header = format!(
"/* automatically generated by rust-bindgen {} */\n\n",
version.unwrap_or("(unknown version)")
);
writer.write_all(header.as_bytes())?;
}
for line in self.options.raw_lines.iter() {
writer.write_all(line.as_bytes())?;
writer.write_all("\n".as_bytes())?;
}
if !self.options.raw_lines.is_empty() {
writer.write_all("\n".as_bytes())?;
}
let bindings = self.module.to_string();
match self.rustfmt_generated_string(&bindings) {
Ok(rustfmt_bindings) => {
writer.write_all(rustfmt_bindings.as_bytes())?;
}
Err(err) => {
eprintln!(
"Failed to run rustfmt: {} (non-fatal, continuing)",
err
);
writer.write_all(bindings.as_bytes())?;
}
}
Ok(())
}
fn rustfmt_path(&self) -> io::Result<Cow<PathBuf>> {
debug_assert!(self.options.rustfmt_bindings);
if let Some(ref p) = self.options.rustfmt_path {
return Ok(Cow::Borrowed(p));
}
if let Ok(rustfmt) = env::var("RUSTFMT") {
return Ok(Cow::Owned(rustfmt.into()));
}
#[cfg(feature = "which-rustfmt")]
match which::which("rustfmt") {
Ok(p) => Ok(Cow::Owned(p)),
Err(e) => {
Err(io::Error::new(io::ErrorKind::Other, format!("{}", e)))
}
}
#[cfg(not(feature = "which-rustfmt"))]
Ok(Cow::Owned("rustfmt".into()))
}
fn rustfmt_generated_string<'a>(
&self,
source: &'a str,
) -> io::Result<Cow<'a, str>> {
let _t = time::Timer::new("rustfmt_generated_string")
.with_output(self.options.time_phases);
if !self.options.rustfmt_bindings {
return Ok(Cow::Borrowed(source));
}
let rustfmt = self.rustfmt_path()?;
let mut cmd = Command::new(&*rustfmt);
cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
if let Some(path) = self
.options
.rustfmt_configuration_file
.as_ref()
.and_then(|f| f.to_str())
{
cmd.args(["--config-path", path]);
}
let mut child = cmd.spawn()?;
let mut child_stdin = child.stdin.take().unwrap();
let mut child_stdout = child.stdout.take().unwrap();
let source = source.to_owned();
let stdin_handle = ::std::thread::spawn(move || {
let _ = child_stdin.write_all(source.as_bytes());
source
});
let mut output = vec![];
io::copy(&mut child_stdout, &mut output)?;
let status = child.wait()?;
let source = stdin_handle.join().expect(
"The thread writing to rustfmt's stdin doesn't do \
anything that could panic",
);
match String::from_utf8(output) {
Ok(bindings) => match status.code() {
Some(0) => Ok(Cow::Owned(bindings)),
Some(2) => Err(io::Error::new(
io::ErrorKind::Other,
"Rustfmt parsing errors.".to_string(),
)),
Some(3) => {
warn!("Rustfmt could not format some lines.");
Ok(Cow::Owned(bindings))
}
_ => Err(io::Error::new(
io::ErrorKind::Other,
"Internal rustfmt error".to_string(),
)),
},
_ => Ok(Cow::Owned(source)),
}
}
#[inline]
pub fn emit_warnings(&self) {
for message in &self.warnings {
println!("cargo:warning={}", message);
}
}
#[inline]
pub fn warnings(&self) -> &[String] {
&self.warnings
}
}
impl std::fmt::Display for Bindings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut bytes = vec![];
self.write(Box::new(&mut bytes) as Box<dyn Write>)
.expect("writing to a vec cannot fail");
f.write_str(
std::str::from_utf8(&bytes)
.expect("we should only write bindings that are valid utf-8"),
)
}
}
fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
ctx.options().builtins || !cursor.is_builtin()
}
fn parse_one(
ctx: &mut BindgenContext,
cursor: clang::Cursor,
parent: Option<ItemId>,
) -> clang_sys::CXChildVisitResult {
if !filter_builtins(ctx, &cursor) {
return CXChildVisit_Continue;
}
use clang_sys::CXChildVisit_Continue;
match Item::parse(cursor, parent, ctx) {
Ok(..) => {}
Err(ParseError::Continue) => {}
Err(ParseError::Recurse) => {
cursor.visit(|child| parse_one(ctx, child, parent));
}
}
CXChildVisit_Continue
}
fn parse(context: &mut BindgenContext) -> Result<(), BindgenError> {
use clang_sys::*;
let mut error = None;
for d in context.translation_unit().diags().iter() {
let msg = d.format();
let is_err = d.severity() >= CXDiagnostic_Error;
if is_err {
let error = error.get_or_insert_with(String::new);
error.push_str(&msg);
error.push('\n');
} else {
eprintln!("clang diag: {}", msg);
}
}
if let Some(message) = error {
return Err(BindgenError::ClangDiagnostic(message));
}
let cursor = context.translation_unit().cursor();
if context.options().emit_ast {
fn dump_if_not_builtin(cur: &clang::Cursor) -> CXChildVisitResult {
if !cur.is_builtin() {
clang::ast_dump(cur, 0)
} else {
CXChildVisit_Continue
}
}
cursor.visit(|cur| dump_if_not_builtin(&cur));
}
let root = context.root_module();
context.with_module(root, |context| {
cursor.visit(|cursor| parse_one(context, cursor, None))
});
assert!(
context.current_module() == context.root_module(),
"How did this happen?"
);
Ok(())
}
#[derive(Debug)]
pub struct ClangVersion {
pub parsed: Option<(u32, u32)>,
pub full: String,
}
pub fn clang_version() -> ClangVersion {
ensure_libclang_is_loaded();
let raw_v: String = clang::extract_clang_version();
let split_v: Option<Vec<&str>> = raw_v
.split_whitespace()
.find(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit()))
.map(|v| v.split('.').collect());
if let Some(v) = split_v {
if v.len() >= 2 {
let maybe_major = v[0].parse::<u32>();
let maybe_minor = v[1].parse::<u32>();
if let (Ok(major), Ok(minor)) = (maybe_major, maybe_minor) {
return ClangVersion {
parsed: Some((major, minor)),
full: raw_v.clone(),
};
}
}
};
ClangVersion {
parsed: None,
full: raw_v.clone(),
}
}
fn get_target_dependent_env_var(var: &str) -> Option<String> {
if let Ok(target) = env::var("TARGET") {
if let Ok(v) = env::var(format!("{}_{}", var, target)) {
return Some(v);
}
if let Ok(v) = env::var(format!("{}_{}", var, target.replace('-', "_")))
{
return Some(v);
}
}
env::var(var).ok()
}
#[derive(Debug)]
pub struct CargoCallbacks;
impl callbacks::ParseCallbacks for CargoCallbacks {
fn include_file(&self, filename: &str) {
println!("cargo:rerun-if-changed={}", filename);
}
}
#[test]
fn commandline_flag_unit_test_function() {
let bindings = crate::builder();
let command_line_flags = bindings.command_line_flags();
let test_cases = vec![
"--rust-target",
"--no-derive-default",
"--generate",
"functions,types,vars,methods,constructors,destructors",
]
.iter()
.map(|&x| x.into())
.collect::<Vec<String>>();
assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
let bindings = crate::builder()
.header("input_header")
.allowlist_type("Distinct_Type")
.allowlist_function("safe_function");
let command_line_flags = bindings.command_line_flags();
let test_cases = vec![
"--rust-target",
"input_header",
"--no-derive-default",
"--generate",
"functions,types,vars,methods,constructors,destructors",
"--allowlist-type",
"Distinct_Type",
"--allowlist-function",
"safe_function",
]
.iter()
.map(|&x| x.into())
.collect::<Vec<String>>();
println!("{:?}", command_line_flags);
assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
}
#[test]
fn test_rust_to_clang_target() {
assert_eq!(rust_to_clang_target("aarch64-apple-ios"), "arm64-apple-ios");
}
#[test]
fn test_rust_to_clang_target_riscv() {
assert_eq!(
rust_to_clang_target("riscv64gc-unknown-linux-gnu"),
"riscv64-unknown-linux-gnu"
)
}
#[test]
fn test_rust_to_clang_target_espidf() {
assert_eq!(
rust_to_clang_target("riscv32imc-esp-espidf"),
"riscv32-esp-elf"
);
assert_eq!(
rust_to_clang_target("xtensa-esp32-espidf"),
"xtensa-esp32-elf"
);
}