aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs182
1 files changed, 139 insertions, 43 deletions
diff --git a/src/lib.rs b/src/lib.rs
index d0253db..9a90dac 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -51,6 +51,7 @@ macro_rules! doc_mod {
mod clang;
mod codegen;
+mod deps;
mod features;
mod ir;
mod parse;
@@ -88,7 +89,7 @@ type HashSet<K> = ::rustc_hash::FxHashSet<K>;
pub(crate) use std::collections::hash_map::Entry;
/// Default prefix for the anon fields.
-pub const DEFAULT_ANON_FIELDS_PREFIX: &'static str = "__bindgen_anon_";
+pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
fn file_is_cpp(name_file: &str) -> bool {
name_file.ends_with(".hpp") ||
@@ -307,6 +308,7 @@ impl Builder {
(&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"),
@@ -316,6 +318,7 @@ impl Builder {
(&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 {
@@ -557,6 +560,14 @@ impl Builder {
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());
+ }
+
// Add clang arguments
output_vector.push("--".into());
@@ -604,6 +615,19 @@ impl Builder {
self
}
+ /// Add a depfile output which will be written alongside the generated bindings.
+ 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
+ }
+
/// Add `contents` as an input C/C++ header named `name`.
///
/// The file `name` will be added to the clang arguments.
@@ -798,6 +822,13 @@ impl Builder {
self
}
+ /// Hide any contents of the given file from the generated bindings,
+ /// regardless of whether it's a type, function, module etc.
+ pub fn blocklist_file<T: AsRef<str>>(mut self, arg: T) -> Builder {
+ self.options.blocklisted_files.insert(arg);
+ self
+ }
+
/// Treat the given type as opaque in the generated bindings. Regular
/// expressions are supported.
///
@@ -1401,11 +1432,22 @@ impl Builder {
self
}
+ /// If true, always emit explicit padding fields.
+ ///
+ /// If a struct needs to be serialized in its native format (padding bytes
+ /// and all), for example writing it to a file or sending it on the network,
+ /// then this should be enabled, as anything reading the padding bytes of
+ /// a struct may lead to Undefined Behavior.
+ pub fn explicit_padding(mut self, doit: bool) -> Self {
+ self.options.force_explicit_padding = doit;
+ self
+ }
+
/// Generate the Rust bindings using the options built up thus far.
pub fn generate(mut self) -> Result<Bindings, ()> {
// Add any extra arguments from the environment to the clang command line.
if let Some(extra_clang_args) =
- env::var("BINDGEN_EXTRA_CLANG_ARGS").ok()
+ get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS")
{
// Try to parse it with shell quoting. If we fail, make it one single big argument.
if let Some(strings) = shlex::split(&extra_clang_args) {
@@ -1417,11 +1459,13 @@ impl Builder {
// Transform input headers to arguments on the clang command line.
self.options.input_header = self.input_headers.pop();
- self.options
- .clang_args
- .extend(self.input_headers.drain(..).flat_map(|header| {
- iter::once("-include".into()).chain(iter::once(header))
- }));
+ self.options.extra_input_headers = self.input_headers;
+ self.options.clang_args.extend(
+ self.options.extra_input_headers.iter().flat_map(|header| {
+ iter::once("-include".into())
+ .chain(iter::once(header.to_string()))
+ }),
+ );
self.options.input_unsaved_files.extend(
self.input_header_contents
@@ -1553,6 +1597,13 @@ impl Builder {
self
}
+ /// Add `#[must_use]` for the given type. Regular
+ /// expressions are supported.
+ pub fn must_use_type<T: Into<String>>(mut self, arg: T) -> Builder {
+ self.options.must_use_types.insert(arg.into());
+ self
+ }
+
/// Set whether `arr[size]` should be treated as `*mut T` or `*mut [T; size]` (same for mut)
pub fn array_pointers_in_arguments(mut self, doit: bool) -> Self {
self.options.array_pointers_in_arguments = doit;
@@ -1600,6 +1651,15 @@ impl Builder {
self.options.translate_enum_integer_types = doit;
self
}
+
+ /// Generate types with C style naming.
+ ///
+ /// This will add prefixes to the generated type names. For example instead of a struct `A` we
+ /// will generate struct `struct_A`. Currently applies to structs, unions, and enums.
+ pub fn c_naming(mut self, doit: bool) -> Self {
+ self.options.c_naming = doit;
+ self
+ }
}
/// Configuration options for generated bindings.
@@ -1617,6 +1677,10 @@ struct BindgenOptions {
/// blocklisted and should not appear in the generated code.
blocklisted_items: RegexSet,
+ /// The set of files whose contents should be blocklisted and should not
+ /// appear in the generated code.
+ blocklisted_files: RegexSet,
+
/// The set of types that should be treated as opaque structures in the
/// generated code.
opaque_types: RegexSet,
@@ -1624,6 +1688,9 @@ struct BindgenOptions {
/// The explicit rustfmt path.
rustfmt_path: Option<PathBuf>,
+ /// The path to which we should write a Makefile-syntax depfile (if any).
+ depfile: Option<deps::DepfileSpec>,
+
/// The set of types that we should have bindings for in the generated
/// code.
///
@@ -1785,6 +1852,9 @@ struct BindgenOptions {
/// The input header file.
input_header: Option<String>,
+ /// Any additional input header files.
+ extra_input_headers: Vec<String>,
+
/// Unsaved files for input.
input_unsaved_files: Vec<clang::UnsavedFile>,
@@ -1878,6 +1948,9 @@ struct BindgenOptions {
/// The set of types that we should not derive `Hash` for.
no_hash_types: RegexSet,
+ /// The set of types that we should be annotated with `#[must_use]`.
+ must_use_types: RegexSet,
+
/// Decide if C arrays should be regular pointers in rust or array pointers
array_pointers_in_arguments: bool,
@@ -1899,6 +1972,12 @@ struct BindgenOptions {
/// Always translate enum integer types to native Rust integer types.
translate_enum_integer_types: bool,
+
+ /// Generate types with C style naming.
+ c_naming: bool,
+
+ /// Always output explicit padding fields
+ force_explicit_padding: bool,
}
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1915,6 +1994,7 @@ impl BindgenOptions {
&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,
@@ -1930,6 +2010,7 @@ impl BindgenOptions {
&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 &mut regex_sets {
@@ -1961,8 +2042,10 @@ impl Default for BindgenOptions {
blocklisted_types: Default::default(),
blocklisted_functions: Default::default(),
blocklisted_items: Default::default(),
+ blocklisted_files: Default::default(),
opaque_types: Default::default(),
rustfmt_path: Default::default(),
+ depfile: Default::default(),
allowlisted_types: Default::default(),
allowlisted_functions: Default::default(),
allowlisted_vars: Default::default(),
@@ -2008,6 +2091,7 @@ impl Default for BindgenOptions {
module_lines: HashMap::default(),
clang_args: vec![],
input_header: None,
+ extra_input_headers: vec![],
input_unsaved_files: vec![],
parse_callbacks: None,
codegen_config: CodegenConfig::all(),
@@ -2032,12 +2116,15 @@ impl Default for BindgenOptions {
no_debug_types: Default::default(),
no_default_types: Default::default(),
no_hash_types: Default::default(),
+ must_use_types: Default::default(),
array_pointers_in_arguments: false,
wasm_import_module_name: None,
dynamic_library_name: None,
dynamic_link_require_all: false,
respect_cxx_access_specs: false,
translate_enum_integer_types: false,
+ c_naming: false,
+ force_explicit_padding: false,
}
}
}
@@ -2075,7 +2162,7 @@ pub struct Bindings {
module: proc_macro2::TokenStream,
}
-pub(crate) const HOST_TARGET: &'static str =
+pub(crate) const HOST_TARGET: &str =
include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
// Some architecture triplets are different between rust and libclang, see #1211
@@ -2083,7 +2170,8 @@ pub(crate) const HOST_TARGET: &'static str =
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["aarch64-apple-".len()..]);
+ clang_target
+ .push_str(rust_target.strip_prefix("aarch64-apple-").unwrap());
return clang_target;
}
rust_target.to_owned()
@@ -2205,10 +2293,7 @@ impl Bindings {
// Whether we are working with C or C++ inputs.
let is_cpp = args_are_cpp(&options.clang_args) ||
- options
- .input_header
- .as_ref()
- .map_or(false, |i| file_is_cpp(&i));
+ options.input_header.as_deref().map_or(false, file_is_cpp);
let search_paths = if is_cpp {
clang.cpp_search_paths
@@ -2296,15 +2381,6 @@ impl Bindings {
})
}
- /// Convert these bindings into source text (with raw lines prepended).
- pub fn to_string(&self) -> String {
- let mut bytes = vec![];
- self.write(Box::new(&mut bytes) as Box<dyn Write>)
- .expect("writing to a vec cannot fail");
- String::from_utf8(bytes)
- .expect("we should only write bindings that are valid utf-8")
- }
-
/// Write these bindings as source text to a file.
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let file = OpenOptions::new()
@@ -2319,7 +2395,7 @@ impl Bindings {
/// Write these bindings as source text to the given `Write`able.
pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> {
if !self.options.disable_header_comment {
- let version = Some("0.58.1");
+ let version = Some("0.59.2");
let header = format!(
"/* automatically generated by rust-bindgen {} */\n\n",
version.unwrap_or("(unknown version)")
@@ -2354,7 +2430,7 @@ impl Bindings {
}
/// Gets the rustfmt path to rustfmt the generated bindings.
- fn rustfmt_path<'a>(&'a self) -> io::Result<Cow<'a, PathBuf>> {
+ 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));
@@ -2445,6 +2521,18 @@ impl Bindings {
}
}
+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"),
+ )
+ }
+}
+
/// Determines whether the given cursor is in any of the files matched by the
/// options.
fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
@@ -2493,7 +2581,7 @@ fn parse(context: &mut BindgenContext) -> Result<(), ()> {
if context.options().emit_ast {
fn dump_if_not_builtin(cur: &clang::Cursor) -> CXChildVisitResult {
if !cur.is_builtin() {
- clang::ast_dump(&cur, 0)
+ clang::ast_dump(cur, 0)
} else {
CXChildVisit_Continue
}
@@ -2530,26 +2618,19 @@ pub fn clang_version() -> ClangVersion {
let raw_v: String = clang::extract_clang_version();
let split_v: Option<Vec<&str>> = raw_v
.split_whitespace()
- .filter(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit()))
- .next()
+ .find(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit()))
.map(|v| v.split('.').collect());
- match split_v {
- Some(v) => {
- if v.len() >= 2 {
- let maybe_major = v[0].parse::<u32>();
- let maybe_minor = v[1].parse::<u32>();
- match (maybe_major, maybe_minor) {
- (Ok(major), Ok(minor)) => {
- return ClangVersion {
- parsed: Some((major, minor)),
- full: raw_v.clone(),
- }
- }
- _ => {}
- }
+ 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(),
+ };
}
}
- None => {}
};
ClangVersion {
parsed: None,
@@ -2557,10 +2638,25 @@ pub fn clang_version() -> ClangVersion {
}
}
+/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found.
+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()
+}
+
/// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed
/// line
///
-/// When running in side a `build.rs` script, this can be used to make cargo invalidate the
+/// When running inside a `build.rs` script, this can be used to make cargo invalidate the
/// generated bindings whenever any of the files included from the header change:
/// ```
/// use bindgen::builder;