mirror of
https://gitee.com/openharmony/third_party_rust_bindgen
synced 2025-01-19 05:34:58 +00:00
Implement cli option for custom derive (#2328)
* custom derives after DeriveInfo * Introduce `TypeKind` instead of `CompKind` * Add tests * Emit CLI flags for callbacks * update changelog * run rustfmt * fix tests * fix features Co-authored-by: Christian Poveda <christian.poveda@ferrous-systems.com>
This commit is contained in:
parent
190a017a10
commit
bca47cd9c2
@ -156,13 +156,18 @@
|
||||
## Changed
|
||||
* Fixed name collisions when having a C `enum` and a `typedef` with the same
|
||||
name.
|
||||
* The `ParseCallbacks::generated_name_override` now receives `ItemInfo<'_>` as
|
||||
* The `ParseCallbacks::generated_name_override` method now receives `ItemInfo<'_>` as
|
||||
argument instead of a `&str`.
|
||||
* Updated the `clang-sys` crate version to 1.4.0 to support clang 15.
|
||||
* The return type is now ommited in signatures of functions returning `void`.
|
||||
* Updated the `clap` dependency for `bindgen-cli` to 4.
|
||||
* Rewrote the `bindgen-cli` argument parser which could introduce unexpected
|
||||
behavior changes.
|
||||
* The `ParseCallbacks::add_derives` method now receives `DeriveInfo<'_>` as
|
||||
argument instead of a `&str`. This type also includes the kind of target type.
|
||||
* Added a new set of flags `--with-derive-custom`,
|
||||
`--with-derive-custom-struct`, `--with-derive-custom-enum` and
|
||||
`--with-derive-custom-enum` to add custom derives from the CLI.
|
||||
|
||||
## Removed
|
||||
|
||||
|
@ -21,7 +21,7 @@ path = "main.rs"
|
||||
name = "bindgen"
|
||||
|
||||
[dependencies]
|
||||
bindgen = { path = "../bindgen", version = "=0.63.0" }
|
||||
bindgen = { path = "../bindgen", version = "=0.63.0", features = ["cli"] }
|
||||
shlex = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
env_logger = { version = "0.9.0", optional = true }
|
||||
|
@ -1,6 +1,7 @@
|
||||
use bindgen::callbacks::TypeKind;
|
||||
use bindgen::{
|
||||
builder, AliasVariation, Builder, CodegenConfig, EnumVariation,
|
||||
MacroTypeVariation, NonCopyUnionStyle, RustTarget,
|
||||
MacroTypeVariation, NonCopyUnionStyle, RegexSet, RustTarget,
|
||||
DEFAULT_ANON_FIELDS_PREFIX, RUST_TARGET_STRINGS,
|
||||
};
|
||||
use clap::Parser;
|
||||
@ -340,6 +341,18 @@ struct BindgenCommand {
|
||||
/// Wrap unsafe operations in unsafe blocks.
|
||||
#[arg(long)]
|
||||
wrap_unsafe_ops: bool,
|
||||
/// Derive custom traits on any kind of type. The <CUSTOM> value must be of the shape <REGEX>=<DERIVE> where <DERIVE> is a coma-separated list of derive macros.
|
||||
#[arg(long, value_name = "CUSTOM")]
|
||||
with_derive_custom: Vec<String>,
|
||||
/// Derive custom traits on a `struct`. The <CUSTOM> value must be of the shape <REGEX>=<DERIVE> where <DERIVE> is a coma-separated list of derive macros.
|
||||
#[arg(long, value_name = "CUSTOM")]
|
||||
with_derive_custom_struct: Vec<String>,
|
||||
/// Derive custom traits on an `enum. The <CUSTOM> value must be of the shape <REGEX>=<DERIVE> where <DERIVE> is a coma-separated list of derive macros.
|
||||
#[arg(long, value_name = "CUSTOM")]
|
||||
with_derive_custom_enum: Vec<String>,
|
||||
/// Derive custom traits on a `union`. The <CUSTOM> value must be of the shape <REGEX>=<DERIVE> where <DERIVE> is a coma-separated list of derive macros.
|
||||
#[arg(long, value_name = "CUSTOM")]
|
||||
with_derive_custom_union: Vec<String>,
|
||||
/// Prints the version, and exits
|
||||
#[arg(short = 'V', long)]
|
||||
version: bool,
|
||||
@ -456,6 +469,10 @@ where
|
||||
merge_extern_blocks,
|
||||
override_abi,
|
||||
wrap_unsafe_ops,
|
||||
with_derive_custom,
|
||||
with_derive_custom_struct,
|
||||
with_derive_custom_enum,
|
||||
with_derive_custom_union,
|
||||
version,
|
||||
clang_args,
|
||||
} = command;
|
||||
@ -894,5 +911,72 @@ where
|
||||
builder = builder.wrap_unsafe_ops(true);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomDeriveCallback {
|
||||
derives: Vec<String>,
|
||||
kind: Option<TypeKind>,
|
||||
regex_set: bindgen::RegexSet,
|
||||
}
|
||||
|
||||
impl bindgen::callbacks::ParseCallbacks for CustomDeriveCallback {
|
||||
fn cli_args(&self) -> Vec<String> {
|
||||
let mut args = vec![];
|
||||
|
||||
let flag = match &self.kind {
|
||||
None => "--with-derive-custom",
|
||||
Some(TypeKind::Struct) => "--with-derive-custom-struct",
|
||||
Some(TypeKind::Enum) => "--with-derive-custom-enum",
|
||||
Some(TypeKind::Union) => "--with-derive-custom-union",
|
||||
};
|
||||
|
||||
let derives = self.derives.join(",");
|
||||
|
||||
for item in self.regex_set.get_items() {
|
||||
args.extend_from_slice(&[
|
||||
flag.to_owned(),
|
||||
format!("{}={}", item, derives),
|
||||
]);
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
|
||||
fn add_derives(
|
||||
&self,
|
||||
info: &bindgen::callbacks::DeriveInfo<'_>,
|
||||
) -> Vec<String> {
|
||||
if self.kind.map(|kind| kind == info.kind).unwrap_or(true) &&
|
||||
self.regex_set.matches(info.name)
|
||||
{
|
||||
return self.derives.clone();
|
||||
}
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
for (custom_derives, kind) in [
|
||||
(with_derive_custom, None),
|
||||
(with_derive_custom_struct, Some(TypeKind::Struct)),
|
||||
(with_derive_custom_enum, Some(TypeKind::Enum)),
|
||||
(with_derive_custom_union, Some(TypeKind::Union)),
|
||||
] {
|
||||
for custom_derive in custom_derives {
|
||||
let (regex, derives) = custom_derive
|
||||
.rsplit_once('=')
|
||||
.expect("Invalid custom derive argument: Missing `=`");
|
||||
let derives = derives.split(',').map(|s| s.to_owned()).collect();
|
||||
|
||||
let mut regex_set = RegexSet::new();
|
||||
regex_set.insert(regex);
|
||||
regex_set.build(false);
|
||||
|
||||
builder = builder.parse_callbacks(Box::new(CustomDeriveCallback {
|
||||
derives,
|
||||
kind,
|
||||
regex_set,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((builder, output, verbose))
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[dev-dependencies]
|
||||
bindgen = { path = "../bindgen" }
|
||||
bindgen = { path = "../bindgen", features = ["cli"] }
|
||||
diff = "0.1"
|
||||
shlex = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
|
115
bindgen-tests/tests/expectations/tests/derive-custom-cli.rs
generated
Normal file
115
bindgen-tests/tests/expectations/tests/derive-custom-cli.rs
generated
Normal file
@ -0,0 +1,115 @@
|
||||
#![allow(
|
||||
dead_code,
|
||||
non_snake_case,
|
||||
non_camel_case_types,
|
||||
non_upper_case_globals
|
||||
)]
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct foo_struct {
|
||||
pub inner: ::std::os::raw::c_int,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_foo_struct() {
|
||||
const UNINIT: ::std::mem::MaybeUninit<foo_struct> =
|
||||
::std::mem::MaybeUninit::uninit();
|
||||
let ptr = UNINIT.as_ptr();
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<foo_struct>(),
|
||||
4usize,
|
||||
concat!("Size of: ", stringify!(foo_struct))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<foo_struct>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(foo_struct))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).inner) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(foo_struct),
|
||||
"::",
|
||||
stringify!(inner)
|
||||
)
|
||||
);
|
||||
}
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Copy)]
|
||||
pub enum foo_enum {
|
||||
inner = 0,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub union foo_union {
|
||||
pub fst: ::std::mem::ManuallyDrop<::std::os::raw::c_int>,
|
||||
pub snd: ::std::mem::ManuallyDrop<f32>,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_foo_union() {
|
||||
const UNINIT: ::std::mem::MaybeUninit<foo_union> =
|
||||
::std::mem::MaybeUninit::uninit();
|
||||
let ptr = UNINIT.as_ptr();
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<foo_union>(),
|
||||
4usize,
|
||||
concat!("Size of: ", stringify!(foo_union))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<foo_union>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(foo_union))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).fst) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(foo_union),
|
||||
"::",
|
||||
stringify!(fst)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).snd) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(foo_union),
|
||||
"::",
|
||||
stringify!(snd)
|
||||
)
|
||||
);
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct non_matching {
|
||||
pub inner: ::std::os::raw::c_int,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_non_matching() {
|
||||
const UNINIT: ::std::mem::MaybeUninit<non_matching> =
|
||||
::std::mem::MaybeUninit::uninit();
|
||||
let ptr = UNINIT.as_ptr();
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<non_matching>(),
|
||||
4usize,
|
||||
concat!("Size of: ", stringify!(non_matching))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<non_matching>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(non_matching))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).inner) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(non_matching),
|
||||
"::",
|
||||
stringify!(inner)
|
||||
)
|
||||
);
|
||||
}
|
14
bindgen-tests/tests/headers/derive-custom-cli.h
Normal file
14
bindgen-tests/tests/headers/derive-custom-cli.h
Normal file
@ -0,0 +1,14 @@
|
||||
// bindgen-flags: --default-enum-style rust --default-non-copy-union-style manually_drop --no-default=".*" --no-hash=".*" --no-partialeq=".*" --no-debug=".*" --no-copy=".*" --with-derive-custom="foo_[^e].*=Clone" --with-derive-custom-struct="foo.*=Default" --with-derive-custom-enum="foo.*=Copy" --with-derive-custom-union="foo.*=Copy"
|
||||
struct foo_struct {
|
||||
int inner;
|
||||
};
|
||||
enum foo_enum {
|
||||
inner = 0
|
||||
};
|
||||
union foo_union {
|
||||
int fst;
|
||||
float snd;
|
||||
};
|
||||
struct non_matching {
|
||||
int inner;
|
||||
};
|
@ -47,6 +47,7 @@ static = ["clang-sys/static"]
|
||||
runtime = ["clang-sys/runtime"]
|
||||
# Dynamically discover a `rustfmt` binary using the `which` crate
|
||||
which-rustfmt = ["which"]
|
||||
cli = []
|
||||
|
||||
# These features only exist for CI testing -- don't use them if you're not hacking
|
||||
# on bindgen!
|
||||
|
@ -25,6 +25,12 @@ impl Default for MacroParsingBehavior {
|
||||
/// A trait to allow configuring different kinds of types in different
|
||||
/// situations.
|
||||
pub trait ParseCallbacks: fmt::Debug {
|
||||
#[cfg(feature = "cli")]
|
||||
#[doc(hidden)]
|
||||
fn cli_args(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
/// This function will be run on every macro that is identified.
|
||||
fn will_parse_macro(&self, _name: &str) -> MacroParsingBehavior {
|
||||
MacroParsingBehavior::Default
|
||||
@ -120,10 +126,24 @@ pub trait ParseCallbacks: fmt::Debug {
|
||||
|
||||
/// Relevant information about a type to which new derive attributes will be added using
|
||||
/// [`ParseCallbacks::add_derives`].
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct DeriveInfo<'a> {
|
||||
/// The name of the type.
|
||||
pub name: &'a str,
|
||||
/// The kind of the type.
|
||||
pub kind: TypeKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// The kind of the current type.
|
||||
pub enum TypeKind {
|
||||
/// The type is a Rust `struct`.
|
||||
Struct,
|
||||
/// The type is a Rust `enum`.
|
||||
Enum,
|
||||
/// The type is a Rust `union`.
|
||||
Union,
|
||||
}
|
||||
|
||||
/// An struct providing information about the item being passed to `ParseCallbacks::generated_name_override`.
|
||||
|
@ -18,6 +18,7 @@ use self::struct_layout::StructLayoutTracker;
|
||||
|
||||
use super::BindgenOptions;
|
||||
|
||||
use crate::callbacks::{DeriveInfo, TypeKind as DeriveTypeKind};
|
||||
use crate::ir::analysis::{HasVtable, Sizedness};
|
||||
use crate::ir::annotations::FieldAccessorKind;
|
||||
use crate::ir::comp::{
|
||||
@ -2100,11 +2101,18 @@ impl CodeGenerator for CompInfo {
|
||||
let mut derives: Vec<_> = derivable_traits.into();
|
||||
derives.extend(item.annotations().derives().iter().map(String::as_str));
|
||||
|
||||
let is_rust_union = is_union && struct_layout.is_rust_union();
|
||||
|
||||
// The custom derives callback may return a list of derive attributes;
|
||||
// add them to the end of the list.
|
||||
let custom_derives = ctx.options().all_callbacks(|cb| {
|
||||
cb.add_derives(&crate::callbacks::DeriveInfo {
|
||||
cb.add_derives(&DeriveInfo {
|
||||
name: &canonical_name,
|
||||
kind: if is_rust_union {
|
||||
DeriveTypeKind::Union
|
||||
} else {
|
||||
DeriveTypeKind::Struct
|
||||
},
|
||||
})
|
||||
});
|
||||
// In most cases this will be a no-op, since custom_derives will be empty.
|
||||
@ -2118,7 +2126,7 @@ impl CodeGenerator for CompInfo {
|
||||
attributes.push(attributes::must_use());
|
||||
}
|
||||
|
||||
let mut tokens = if is_union && struct_layout.is_rust_union() {
|
||||
let mut tokens = if is_rust_union {
|
||||
quote! {
|
||||
#( #attributes )*
|
||||
pub union #canonical_ident
|
||||
@ -3112,7 +3120,10 @@ impl CodeGenerator for Enum {
|
||||
// The custom derives callback may return a list of derive attributes;
|
||||
// add them to the end of the list.
|
||||
let custom_derives = ctx.options().all_callbacks(|cb| {
|
||||
cb.add_derives(&crate::callbacks::DeriveInfo { name: &name })
|
||||
cb.add_derives(&DeriveInfo {
|
||||
name: &name,
|
||||
kind: DeriveTypeKind::Enum,
|
||||
})
|
||||
});
|
||||
// In most cases this will be a no-op, since custom_derives will be empty.
|
||||
derives.extend(custom_derives.iter().map(|s| s.as_str()));
|
||||
|
@ -65,7 +65,7 @@ mod clang;
|
||||
mod codegen;
|
||||
mod deps;
|
||||
mod features;
|
||||
mod ir;
|
||||
pub mod ir;
|
||||
mod parse;
|
||||
mod regex_set;
|
||||
mod time;
|
||||
@ -91,7 +91,7 @@ use crate::ir::context::{BindgenContext, ItemId};
|
||||
pub use crate::ir::function::Abi;
|
||||
use crate::ir::item::Item;
|
||||
use crate::parse::ParseError;
|
||||
use crate::regex_set::RegexSet;
|
||||
pub use crate::regex_set::RegexSet;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
@ -653,6 +653,11 @@ impl Builder {
|
||||
output_vector.push("--wrap-unsafe-ops".into());
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
for callbacks in &self.options.parse_callbacks {
|
||||
output_vector.extend(callbacks.cli_args());
|
||||
}
|
||||
|
||||
// Add clang arguments
|
||||
|
||||
output_vector.push("--".into());
|
||||
|
@ -16,6 +16,13 @@ pub struct RegexSet {
|
||||
}
|
||||
|
||||
impl RegexSet {
|
||||
/// Create a new RegexSet
|
||||
pub fn new() -> RegexSet {
|
||||
RegexSet {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this set empty?
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
|
Loading…
x
Reference in New Issue
Block a user