//! Generate Rust bindings for C and C++ libraries. //! //! Provide a C/C++ header file, receive Rust FFI code to call into C/C++ //! functions and use types defined in the header. //! //! See the [`Builder`](./struct.Builder.html) struct for usage. //! //! See the [Users Guide](https://rust-lang.github.io/rust-bindgen/) for //! additional documentation. #![deny(missing_docs)] #![deny(unused_extern_crates)] #![deny(clippy::disallowed_methods)] // To avoid rather annoying warnings when matching with CXCursor_xxx as a // constant. #![allow(non_upper_case_globals)] // `quote!` nests quite deeply. #![recursion_limit = "128"]
use codegen::CodegenError; use features::RustFeatures; use ir::comment; use ir::context::{BindgenContext, ItemId}; use ir::item::Item; use options::BindgenOptions; use parse::ParseError;
use std::borrow::Cow; use std::collections::hash_map::Entry; use std::env; use std::ffi::OsStr; use std::fs::{File, OpenOptions}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::rc::Rc; use std::str::FromStr;
// Some convenient typedefs for a fast hash map and hash set. type HashMap<K, V> = rustc_hash::FxHashMap<K, V>; type HashSet<K> = rustc_hash::FxHashSet<K>;
/// Default prefix for the anon fields. pubconst DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
/// Formatting tools that can be used to format the bindings #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] pubenum Formatter { /// Do not format the bindings.
None, /// Use `rustfmt` to format the bindings.
Rustfmt, #[cfg(feature = "prettyplease")] /// Use `prettyplease` to format the bindings.
Prettyplease,
}
/// Configure and generate Rust bindings for a C/C++ header. /// /// This is the main entry point to the library. /// /// ```ignore /// use bindgen::builder; /// /// // Configure and generate bindings. /// let bindings = builder().header("path/to/input/header") /// .allowlist_type("SomeCoolClass") /// .allowlist_function("do_some_cool_thing") /// .generate()?; /// /// // Write the generated bindings to an output file. /// bindings.write_to_file("path/to/output.rs")?; /// ``` /// /// # Enums /// /// Bindgen can map C/C++ enums into Rust in different ways. The way bindgen maps enums depends on /// the pattern passed to several methods: /// /// 1. [`constified_enum_module()`](#method.constified_enum_module) /// 2. [`bitfield_enum()`](#method.bitfield_enum) /// 3. [`newtype_enum()`](#method.newtype_enum) /// 4. [`rustified_enum()`](#method.rustified_enum) /// /// For each C enum, bindgen tries to match the pattern in the following order: /// /// 1. Constified enum module /// 2. Bitfield enum /// 3. Newtype enum /// 4. Rustified enum /// /// If none of the above patterns match, then bindgen will generate a set of Rust constants. /// /// # Clang arguments /// /// Extra arguments can be passed to with clang: /// 1. [`clang_arg()`](#method.clang_arg): takes a single argument /// 2. [`clang_args()`](#method.clang_args): takes an iterator of arguments /// 3. `BINDGEN_EXTRA_CLANG_ARGS` environment variable: whitespace separate /// environment variable of arguments /// /// Clang arguments specific to your crate should be added via the /// `clang_arg()`/`clang_args()` methods. /// /// End-users of the crate may need to set the `BINDGEN_EXTRA_CLANG_ARGS` environment variable to /// add additional arguments. For example, to build against a different sysroot a user could set /// `BINDGEN_EXTRA_CLANG_ARGS` to `--sysroot=/path/to/sysroot`. /// /// # Regular expression arguments /// /// Some [`Builder`] methods, such as `allowlist_*` and `blocklist_*`, allow regular /// expressions as arguments. These regular expressions will be enclosed in parentheses and /// anchored with `^` and `$`. So, if the argument passed is `<regex>`, the regular expression to be /// stored will be `^(<regex>)$`. /// /// As a consequence, regular expressions passed to `bindgen` will try to match the whole name of /// an item instead of a section of it, which means that to match any items with the prefix /// `prefix`, the `prefix.*` regular expression must be used. /// /// Certain methods, like [`Builder::allowlist_function`], use regular expressions over function /// names. To match C++ methods, prefix the name of the type where they belong, followed by an /// underscore. So, if the type `Foo` has a method `bar`, it can be matched with the `Foo_bar` /// regular expression. /// /// Additionally, Objective-C interfaces can be matched by prefixing the regular expression with /// `I`. For example, the `IFoo` regular expression matches the `Foo` interface, and the `IFoo_foo` /// regular expression matches the `foo` method of the `Foo` interface. /// /// Releases of `bindgen` with a version lesser or equal to `0.62.0` used to accept the wildcard /// pattern `*` as a valid regular expression. This behavior has been deprecated, and the `.*` /// regular expression must be used instead. #[derive(Debug, Default, Clone)] pubstruct Builder {
options: BindgenOptions,
}
/// Construct a new [`Builder`](./struct.Builder.html). pubfn builder() -> Builder {
Default::default()
}
fn get_extra_clang_args(
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
) -> Vec<String> { // Add any extra arguments from the environment to the clang command line. let extra_clang_args = match get_target_dependent_env_var(
parse_callbacks, "BINDGEN_EXTRA_CLANG_ARGS",
) {
None => return vec![],
Some(s) => s,
};
// Try to parse it with shell quoting. If we fail, make it one single big argument. iflet Some(strings) = shlex::split(&extra_clang_args) { return strings;
}
vec![extra_clang_args]
}
impl Builder { /// Generate the Rust bindings using the options built up thus far. pubfn generate(mutself) -> Result<Bindings, BindgenError> { // Add any extra arguments from the environment to the clang command line. self.options.clang_args.extend(
get_extra_clang_args(&self.options.parse_callbacks)
.into_iter()
.map(String::into_boxed_str),
);
for header in &self.options.input_headers { self.options
.for_each_callback(|cb| cb.header_file(header.as_ref()));
}
// Transform input headers to arguments on the clang command line. self.options.clang_args.extend( self.options.input_headers
[..self.options.input_headers.len().saturating_sub(1)]
.iter()
.flat_map(|header| ["-include".into(), header.clone()]),
);
/// Preprocess and dump the input header files to disk. /// /// This is useful when debugging bindgen, using C-Reduce, or when filing /// issues. The resulting file will be named something like `__bindgen.i` or /// `__bindgen.ii` pubfn 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",
)
})?;
// The contents of a wrapper file that includes all the input header // files. letmut wrapper_contents = String::new();
// Whether we are working with C or C++ inputs. letmut is_cpp = args_are_cpp(&self.options.clang_args);
// For each input header, add `#include "$header"`. for header in &self.options.input_headers {
is_cpp |= file_is_cpp(header);
// For each input header content, add a prefix line of `#line 0 "$name"` // followed by the contents. for (name, contents) in &self.options.input_header_contents {
is_cpp |= file_is_cpp(name);
fn deprecated_target_diagnostic(target: RustTarget, _options: &BindgenOptions) {
warn!("The {} Rust target is deprecated. If you have a need to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", target);
#[cfg(feature = "experimental")] if _options.emit_diagnostics { usecrate::diagnostics::{Diagnostic, Level};
letmut diagnostic = Diagnostic::default();
diagnostic.with_title(
format!("The {} Rust target is deprecated.", target),
Level::Warn,
);
diagnostic.add_annotation( "This Rust target was passed to `--rust-target`",
Level::Info,
);
diagnostic.add_annotation("If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", Level::Help);
diagnostic.display();
}
}
// XXX (issue #350): Ensure that our dynamically loaded `libclang` // doesn't get dropped prematurely, nor is loaded multiple times // across different threads.
lazy_static! { staticref 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!",
)
};
}
/// Error type for rust-bindgen. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] pubenum BindgenError { /// The header was a folder.
FolderAsHeader(PathBuf), /// Permissions to read the header is insufficient.
InsufficientPermissions(PathBuf), /// The header does not exist.
NotExist(PathBuf), /// Clang diagnosed an error.
ClangDiagnostic(String), /// Code generation reported an error.
Codegen(CodegenError),
}
let (effective_target, explicit_target) =
find_effective_target(&options.clang_args);
let is_host_build =
rust_to_clang_target(HOST_TARGET) == effective_target;
// NOTE: The is_host_build check wouldn't be sound normally in some // cases if we were to call a binary (if you have a 32-bit clang and are // building on a 64-bit system for example). But since we rely on // opening libclang.so, it has to be the same architecture and thus the // check is fine. if !explicit_target && !is_host_build {
options.clang_args.insert( 0,
format!("--target={}", effective_target).into_boxed_str(),
);
};
fn detect_include_paths(options: &mut BindgenOptions) { if !options.detect_include_paths { return;
}
// Filter out include paths and similar stuff, so we don't incorrectly // promote them to `-isystem`. let clang_args_for_clang_sys = { letmut last_was_include_prefix = false;
options
.clang_args
.iter()
.filter(|arg| { if last_was_include_prefix {
last_was_include_prefix = false; returnfalse;
}
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);
// Whether we are working with C or C++ inputs. 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
};
{ let _t = time::Timer::new("parse").with_output(time_phases);
parse(&mut context)?;
}
let (module, options) =
codegen::codegen(context).map_err(BindgenError::Codegen)?;
Ok(Bindings { options, module })
}
/// Write these bindings as source text to a file. pubfn 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(())
}
/// Write these bindings as source text to the given `Write`able. pubfn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> { const NL: &str = if cfg!(windows) { "\r\n" } else { "\n" };
if !self.options.disable_header_comment { let version =
option_env!("CARGO_PKG_VERSION").unwrap_or("(unknown version)");
writeln!(
writer, "/* automatically generated by rust-bindgen {version} */{NL}",
)?;
}
for line inself.options.raw_lines.iter() {
writer.write_all(line.as_bytes())?;
writer.write_all(NL.as_bytes())?;
}
if !self.options.raw_lines.is_empty() {
writer.write_all(NL.as_bytes())?;
}
/// Gets the rustfmt path to rustfmt the generated bindings. fn rustfmt_path(&self) -> io::Result<Cow<PathBuf>> {
debug_assert!(matches!(self.options.formatter, Formatter::Rustfmt)); iflet Some(ref p) = self.options.rustfmt_path { return Ok(Cow::Borrowed(p));
} iflet 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"))] // No rustfmt binary was specified, so assume that the binary is called // "rustfmt" and that it is in the user's PATH.
Ok(Cow::Owned("rustfmt".into()))
}
/// Formats a token stream with the formatter set up in `BindgenOptions`. fn format_tokens(
&self,
tokens: &proc_macro2::TokenStream,
) -> io::Result<String> { let _t = time::Timer::new("rustfmt_generated_string")
.with_output(self.options.time_phases);
// Write to stdin in a new thread, so that we can read from stdout on this // thread. This keeps the child from blocking on writing to its stdout which // might block us from writing to its stdin. let stdin_handle = ::std::thread::spawn(move || { let _ = child_stdin.write_all(source.as_bytes());
source
});
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(bindings),
Some(2) => Err(io::Error::new(
io::ErrorKind::Other, "Rustfmt parsing errors.".to_string(),
)),
Some(3) => {
rustfmt_non_fatal_error_diagnostic( "Rustfmt could not format some lines",
&self.options,
);
Ok(bindings)
}
_ => Err(io::Error::new(
io::ErrorKind::Other, "Internal rustfmt error".to_string(),
)),
},
_ => Ok(source),
}
}
}
#[cfg(feature = "experimental")] if _options.emit_diagnostics { usecrate::diagnostics::{Diagnostic, Level};
Diagnostic::default()
.with_title(msg, Level::Warn)
.add_annotation( "The bindings will be generated but not formatted.",
Level::Note,
)
.display();
}
}
impl std::fmt::Display for Bindings { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { letmut bytes = vec![]; self.write(Box::new(&mut bytes) asBox<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 {
ctx.options().builtins || !cursor.is_builtin()
}
/// Parse one `Item` from the Clang cursor. fn parse_one(
ctx: &mut BindgenContext,
cursor: clang::Cursor,
parent: Option<ItemId>,
) { if !filter_builtins(ctx, &cursor) { return;
}
assert!(
context.current_module() == context.root_module(), "How did this happen?"
);
Ok(())
}
/// Extracted Clang version data #[derive(Debug)] pubstruct ClangVersion { /// Major and minor semver, if parsing was successful pub parsed: Option<(u32, u32)>, /// full version string pub full: String,
}
/// Get the major and the minor semver numbers of Clang's version pubfn clang_version() -> ClangVersion {
ensure_libclang_is_loaded();
/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found. fn get_target_dependent_env_var(
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
var: &str,
) -> Option<String> { iflet Ok(target) = env_var(parse_callbacks, "TARGET") { iflet Ok(v) = env_var(parse_callbacks, format!("{}_{}", var, target)) { return Some(v);
} iflet Ok(v) = env_var(
parse_callbacks,
format!("{}_{}", var, target.replace('-', "_")),
) { return Some(v);
}
}
env_var(parse_callbacks, var).ok()
}
/// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed /// line and on env variable usage by echoing a rerun-if-env-changed line /// /// 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; /// let bindings = builder() /// .header("path/to/input/header") /// .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) /// .generate(); /// ``` #[derive(Debug)] pubstruct CargoCallbacks {
rerun_on_header_files: bool,
}
/// Create a new `CargoCallbacks` value with [`CargoCallbacks::rerun_on_header_files`] disabled. /// /// This constructor has been deprecated in favor of [`CargoCallbacks::new`] where /// [`CargoCallbacks::rerun_on_header_files`] is enabled by default. #[deprecated = "Use `CargoCallbacks::new()` instead. Please, check the documentation for further information."] pubconst CargoCallbacks: CargoCallbacks = CargoCallbacks {
rerun_on_header_files: false,
};
impl CargoCallbacks { /// Create a new `CargoCallbacks` value. pubfn new() -> Self { Self {
rerun_on_header_files: true,
}
}
/// Whether Cargo should re-run the build script if any of the input header files has changed. /// /// This option is enabled by default unless the deprecated [`const@CargoCallbacks`] /// constructor is used. pubfn rerun_on_header_files(mutself, doit: bool) -> Self { self.rerun_on_header_files = doit; self
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.