From 2ad5e7cbecfda8ebbf191c4c693875575f9d1635 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 7 Jul 2023 15:19:55 +0200 Subject: [PATCH 1/8] Generate wrapping functions for functional macros --- bindgen-cli/options.rs | 89 ++++++- bindgen-integration/build.rs | 2 +- bindgen-tests/tests/tests.rs | 2 +- bindgen/codegen/mod.rs | 78 +++++- bindgen/codegen/serialize.rs | 41 ++- bindgen/function_types.rs | 466 +++++++++++++++++++++++++++++++++++ bindgen/ir/function.rs | 91 +++++++ bindgen/ir/item.rs | 2 + bindgen/lib.rs | 1 + bindgen/options/mod.rs | 74 +++++- 10 files changed, 814 insertions(+), 32 deletions(-) create mode 100644 bindgen/function_types.rs diff --git a/bindgen-cli/options.rs b/bindgen-cli/options.rs index 66bb3f1439..acb5909f7b 100644 --- a/bindgen-cli/options.rs +++ b/bindgen-cli/options.rs @@ -1,4 +1,5 @@ use bindgen::callbacks::TypeKind; +use bindgen::function_types::{FunctionType, TypeKind as CTypeKind}; use bindgen::{ builder, Abi, AliasVariation, Builder, CodegenConfig, EnumVariation, FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle, @@ -415,14 +416,17 @@ struct BindgenCommand { /// Generate wrappers for `static` and `static inline` functions. #[arg(long, requires = "experimental")] wrap_static_fns: bool, - /// Sets the PATH for the source file that must be created due to the presence of `static` and - /// `static inline` functions. + /// Sets the path of the file where generated native code will be emitted. #[arg(long, requires = "experimental", value_name = "PATH")] - wrap_static_fns_path: Option, + native_code_generation_path: Option, /// Sets the SUFFIX added to the extern wrapper functions generated for `static` and `static /// inline` functions. #[arg(long, requires = "experimental", value_name = "SUFFIX")] wrap_static_fns_suffix: Option, + /// Create a wrapper function for the macro. The MACRO value must be of the shape + /// `[] [()]`. + #[arg(long, value_name = "MACRO")] + macro_function: Option>, /// Set the default VISIBILITY of fields, including bitfields and accessor methods for /// bitfields. This flag is ignored if the `--respect-cxx-access-specs` flag is used. #[arg(long, value_name = "VISIBILITY")] @@ -559,8 +563,9 @@ where with_derive_custom_enum, with_derive_custom_union, wrap_static_fns, - wrap_static_fns_path, + native_code_generation_path, wrap_static_fns_suffix, + macro_function, default_visibility, emit_diagnostics, generate_shell_completions, @@ -1102,14 +1107,61 @@ where builder = builder.wrap_static_fns(true); } - if let Some(path) = wrap_static_fns_path { - builder = builder.wrap_static_fns_path(path); + if let Some(path) = native_code_generation_path { + builder = builder.native_code_generation_path(path); } if let Some(suffix) = wrap_static_fns_suffix { builder = builder.wrap_static_fns_suffix(suffix); } + if let Some(macro_functions) = macro_function { + for macro_definition in macro_functions.into_iter() { + let (left_side, argument_types) = if !macro_definition + .ends_with(')') + { + (macro_definition.as_str(), Vec::new()) + } else { + let Some((left_side, arguments)) = + macro_definition.split_once('(') + else { + panic!("Invalid function macro definition: No '(' for ')' at end found"); + }; + let arguments = &arguments[..arguments.len() - 1]; + if arguments.trim().is_empty() { + // The empty argument list case `()`. + (left_side, Vec::new()) + } else { + ( + left_side, + arguments + .split(',') + .map(|argument| { + parse_c_type(argument).unwrap_or_else(|err| { + panic!( + "Invalid function macro argument type: {}", + err + ) + }) + }) + .collect(), + ) + } + }; + let parts = + left_side.split_ascii_whitespace().collect::>(); + let (return_type, name) = match parts.as_slice() { + [name] => (CTypeKind::Void, name), + [return_type, name] => (parse_c_type(return_type).unwrap_or_else(|err| panic!("Invalid function macro return type: {}", err)), name), + _ => panic!("Invalid function macro name: Name must not contain whitespaces"), + }; + builder = builder.macro_function( + *name, + FunctionType::from(return_type, argument_types), + ) + } + } + if let Some(visibility) = default_visibility { builder = builder.default_visibility(visibility); } @@ -1120,3 +1172,28 @@ where Ok((builder, output, verbose)) } + +/// Parse a [`CTypeKind`] from the given command line `input`. +fn parse_c_type(input: &str) -> Result { + let input = input.trim(); + Ok(match input { + "void" | "()" => CTypeKind::Void, + "bool" => CTypeKind::Bool, + "char" => CTypeKind::Char, + "int" => CTypeKind::Int, + "long" => CTypeKind::Long, + "u8" | "uint8_t" => CTypeKind::U8, + "u16" | "uint16_t" => CTypeKind::U16, + "u32" | "uint32_t" => CTypeKind::U32, + "u64" | "uint64_t" => CTypeKind::U64, + "u128" | "uint128_t" => CTypeKind::U128, + "i8" | "int8_t" => CTypeKind::I8, + "i16" | "int16_t" => CTypeKind::I16, + "i32" | "int32_t" => CTypeKind::I32, + "i64" | "int64_t" => CTypeKind::I64, + "i128" | "int128_t" => CTypeKind::I128, + "f32" | "float" => CTypeKind::F32, + "f64" | "double" => CTypeKind::F64, + _ => return Err(format!("Unsupported C type \"{input}\"")), + }) +} diff --git a/bindgen-integration/build.rs b/bindgen-integration/build.rs index 7021f865d7..971959cd9b 100644 --- a/bindgen-integration/build.rs +++ b/bindgen-integration/build.rs @@ -234,7 +234,7 @@ fn setup_wrap_static_fns_test() { .parse_callbacks(Box::new(CargoCallbacks)) .parse_callbacks(Box::new(WrappedVaListCallback)) .wrap_static_fns(true) - .wrap_static_fns_path( + .native_code_generation_path( out_path.join("wrap_static_fns").display().to_string(), ) .clang_arg("-DUSE_VA_HEADER") diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs index 0e15155d92..25649421fc 100644 --- a/bindgen-tests/tests/tests.rs +++ b/bindgen-tests/tests/tests.rs @@ -690,7 +690,7 @@ fn test_wrap_static_fns() { let _bindings = Builder::default() .header("tests/headers/wrap-static-fns.h") .wrap_static_fns(true) - .wrap_static_fns_path(generated_path.display().to_string()) + .native_code_generation_path(generated_path.display().to_string()) .parse_callbacks(Box::new(parse_callbacks::WrapAsVariadicFn)) .generate() .expect("Failed to generate bindings"); diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index dd1486df74..f247895932 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -228,6 +228,15 @@ struct WrapAsVariadic { idx_of_va_list_arg: usize, } +/// An item that can be serialized to C code. +enum SerializableItem { + /// A static function with optionally the argument for the wrap as + /// variadic transformation to be applied. + StaticFunction(ItemId, Option), + /// A function like macro. + MacroFunction(ItemId), +} + struct CodegenResult<'a> { items: Vec, dynamic_items: DynamicItems, @@ -276,9 +285,8 @@ struct CodegenResult<'a> { /// that name. This lets us give each overload a unique suffix. overload_counters: HashMap, - /// List of items to serialize. With optionally the argument for the wrap as - /// variadic transformation to be applied. - items_to_serialize: Vec<(ItemId, Option)>, + /// List of items to serialize. + items_to_serialize: Vec, } impl<'a> CodegenResult<'a> { @@ -4117,6 +4125,12 @@ impl CodeGenerator for Function { debug!("::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); + if self.kind() == FunctionKind::Macro { + result + .items_to_serialize + .push(SerializableItem::MacroFunction(item.id())); + } + let is_internal = matches!(self.linkage(), Linkage::Internal); let signature_item = ctx.resolve_item(self.signature()); @@ -4310,7 +4324,10 @@ impl CodeGenerator for Function { if should_wrap { result .items_to_serialize - .push((item.id(), wrap_as_variadic)); + .push(SerializableItem::StaticFunction( + item.id(), + wrap_as_variadic, + )); } // If we're doing dynamic binding generation, add to the dynamic items. @@ -4765,7 +4782,9 @@ pub(crate) fn codegen( pub(crate) mod utils { use super::serialize::CSerialize; - use super::{error, CodegenError, CodegenResult, ToRustTyOrOpaque}; + use super::{ + error, CodegenError, CodegenResult, SerializableItem, ToRustTyOrOpaque, + }; use crate::ir::context::BindgenContext; use crate::ir::context::TypeId; use crate::ir::function::{Abi, ClangAbi, FunctionSig}; @@ -4788,7 +4807,7 @@ pub(crate) mod utils { let path = context .options() - .wrap_static_fns_path + .native_code_generation_path .as_ref() .map(PathBuf::from) .unwrap_or_else(|| { @@ -4812,6 +4831,7 @@ pub(crate) mod utils { let mut code = Vec::new(); + writeln!(code, "#include \n#include ")?; if !context.options().input_headers.is_empty() { for header in &context.options().input_headers { writeln!(code, "#include \"{}\"", header)?; @@ -4828,11 +4848,49 @@ pub(crate) mod utils { writeln!(code)?; } - writeln!(code, "// Static wrappers\n")?; + let mut static_wrappers = result + .items_to_serialize + .iter() + .filter_map(|item| { + if let SerializableItem::StaticFunction(id, wrap_as_variadic) = + item + { + Some((id, wrap_as_variadic)) + } else { + None + } + }) + .peekable(); + if static_wrappers.peek().is_some() { + writeln!(code, "// Static wrappers\n")?; + for (id, wrap_as_variadic) in static_wrappers { + let item = context.resolve_item(*id); + item.serialize( + context, + wrap_as_variadic, + &mut vec![], + &mut code, + )?; + } + } - for (id, wrap_as_variadic) in &result.items_to_serialize { - let item = context.resolve_item(*id); - item.serialize(context, wrap_as_variadic, &mut vec![], &mut code)?; + let mut function_macros = result + .items_to_serialize + .iter() + .filter_map(|item| { + if let SerializableItem::MacroFunction(id) = item { + Some(id) + } else { + None + } + }) + .peekable(); + if function_macros.peek().is_some() { + writeln!(code, "// Macro function wrappers\n")?; + for id in function_macros { + let item = context.resolve_item(*id); + item.serialize(context, &None, &mut vec![], &mut code)?; + } } std::fs::write(source_path, code)?; diff --git a/bindgen/codegen/serialize.rs b/bindgen/codegen/serialize.rs index 7f00f809f4..9c8c99cb54 100644 --- a/bindgen/codegen/serialize.rs +++ b/bindgen/codegen/serialize.rs @@ -62,7 +62,9 @@ impl<'a> CSerialize<'a> for Function { stack: &mut Vec, writer: &mut W, ) -> Result<(), CodegenError> { - if self.kind() != FunctionKind::Function { + if self.kind() != FunctionKind::Function && + self.kind() != FunctionKind::Macro + { return Err(CodegenError::Serialize { msg: format!( "Cannot serialize function kind {:?}", @@ -113,8 +115,19 @@ impl<'a> CSerialize<'a> for Function { .collect::>() }; + let should_add_parenthesis_to_call = + self.kind() != FunctionKind::Macro || !args.is_empty(); + // The name used for the wrapper self. - let wrap_name = format!("{}{}", name, ctx.wrap_static_fns_suffix()); + let wrap_name = format!( + "{}{}", + name, + if self.kind() == FunctionKind::Macro { + "__macro" + } else { + ctx.wrap_static_fns_suffix() + } + ); // The function's return type let (ret_item, ret_ty) = { @@ -137,9 +150,9 @@ impl<'a> CSerialize<'a> for Function { if wrap_as_variadic.is_none() { // Write `) { name(` if the function returns void and `) { return name(` if it does not. if ret_ty.is_void() { - write!(writer, ") {{ {}(", name)?; + write!(writer, ") {{ {}", name)?; } else { - write!(writer, ") {{ return {}(", name)?; + write!(writer, ") {{ return {}", name)?; } } else { // Write `, ...) {` @@ -165,7 +178,10 @@ impl<'a> CSerialize<'a> for Function { if !ret_ty.is_void() { write!(writer, "ret = ")?; } - write!(writer, "{}(", name)?; + write!(writer, "{}", name)?; + } + if should_add_parenthesis_to_call { + write!(writer, "(")?; } // Get the arguments names and insert at the right place if necessary `ap` @@ -181,8 +197,11 @@ impl<'a> CSerialize<'a> for Function { serialize_sep(", ", args.iter(), ctx, writer, |name, _, buf| { write!(buf, "{}", name).map_err(From::from) })?; + if should_add_parenthesis_to_call { + write!(writer, ")")?; + } #[rustfmt::skip] - write!(writer, ");{}", if wrap_as_variadic.is_none() { " " } else { "\n" })?; + write!(writer, ";{}", if wrap_as_variadic.is_none() { " " } else { "\n" })?; if wrap_as_variadic.is_some() { // End va_list and return the result if their is one @@ -254,6 +273,16 @@ impl<'a> CSerialize<'a> for Type { IntKind::LongLong => write!(writer, "long long")?, IntKind::ULongLong => write!(writer, "unsigned long long")?, IntKind::Char { .. } => write!(writer, "char")?, + IntKind::U8 { .. } => write!(writer, "uint8_t")?, + IntKind::U16 { .. } => write!(writer, "uint16_t")?, + IntKind::U32 { .. } => write!(writer, "uint32_t")?, + IntKind::U64 { .. } => write!(writer, "uint64_t")?, + IntKind::U128 { .. } => write!(writer, "uint128_t")?, + IntKind::I8 { .. } => write!(writer, "int8_t")?, + IntKind::I16 { .. } => write!(writer, "int16_t")?, + IntKind::I32 { .. } => write!(writer, "int32_t")?, + IntKind::I64 { .. } => write!(writer, "int64_t")?, + IntKind::I128 { .. } => write!(writer, "int128_t")?, int_kind => { return Err(CodegenError::Serialize { msg: format!( diff --git a/bindgen/function_types.rs b/bindgen/function_types.rs new file mode 100644 index 0000000000..22105994e9 --- /dev/null +++ b/bindgen/function_types.rs @@ -0,0 +1,466 @@ +//! A public API for defining C function types. + +use crate::ir::ty::TypeKind as InternalTypeKind; + +/// A C value type. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TypeKind { + /// Represents the C `void` type. + Void, + /// Represents the C `bool` type. + Bool, + /// Represents the C `char` type. + Char, + /// Represents the C `int` type. + Int, + /// Represents the C `long` type. + Long, + /// Represents the C `uint8_t` type. + U8, + /// Represents the C `uint16_t` type. + U16, + /// Represents the C `uint32_t` type. + U32, + /// Represents the C `uint64_t` type. + U64, + /// Represents the C `uint128_t` type. + U128, + /// Represents the C `int8_t` type. + I8, + /// Represents the C `int16_t` type. + I16, + /// Represents the C `int32_t` type. + I32, + /// Represents the C `int64_t` type. + I64, + /// Represents the C `int128_t` type. + I128, + /// Represents the C `float` type. + F32, + /// Represents the C `double` type. + F64, +} + +impl From for InternalTypeKind { + fn from(val: TypeKind) -> Self { + match val { + TypeKind::Void => InternalTypeKind::Void, + TypeKind::Bool => { + InternalTypeKind::Int(crate::callbacks::IntKind::Bool) + } + TypeKind::Char => { + InternalTypeKind::Int(crate::callbacks::IntKind::Char { + is_signed: false, + }) + } + TypeKind::Int => { + InternalTypeKind::Int(crate::callbacks::IntKind::Int) + } + TypeKind::Long => { + InternalTypeKind::Int(crate::callbacks::IntKind::Long) + } + TypeKind::U8 => { + InternalTypeKind::Int(crate::callbacks::IntKind::U8) + } + TypeKind::U16 => { + InternalTypeKind::Int(crate::callbacks::IntKind::U16) + } + TypeKind::U32 => { + InternalTypeKind::Int(crate::callbacks::IntKind::U32) + } + TypeKind::U64 => { + InternalTypeKind::Int(crate::callbacks::IntKind::U64) + } + TypeKind::U128 => { + InternalTypeKind::Int(crate::callbacks::IntKind::U128) + } + TypeKind::I8 => { + InternalTypeKind::Int(crate::callbacks::IntKind::I8) + } + TypeKind::I16 => { + InternalTypeKind::Int(crate::callbacks::IntKind::I16) + } + TypeKind::I32 => { + InternalTypeKind::Int(crate::callbacks::IntKind::I32) + } + TypeKind::I64 => { + InternalTypeKind::Int(crate::callbacks::IntKind::I64) + } + TypeKind::I128 => { + InternalTypeKind::Int(crate::callbacks::IntKind::I128) + } + TypeKind::F32 => { + InternalTypeKind::Float(crate::ir::ty::FloatKind::Float) + } + TypeKind::F64 => { + InternalTypeKind::Float(crate::ir::ty::FloatKind::Double) + } + } + } +} + +/// Something that represents a C value type. +pub trait CType { + /// Get the [TypeKind] that this C type represents. + fn as_type() -> TypeKind; +} + +// Allow `()` to be used to indicate `void`. +impl CType for () { + fn as_type() -> TypeKind { + TypeKind::Void + } +} + +impl CType for bool { + fn as_type() -> TypeKind { + TypeKind::Bool + } +} + +impl CType for char { + fn as_type() -> TypeKind { + TypeKind::Char + } +} + +impl CType for u8 { + fn as_type() -> TypeKind { + TypeKind::U8 + } +} + +impl CType for u16 { + fn as_type() -> TypeKind { + TypeKind::U16 + } +} + +impl CType for u32 { + fn as_type() -> TypeKind { + TypeKind::U32 + } +} + +impl CType for u64 { + fn as_type() -> TypeKind { + TypeKind::U64 + } +} + +impl CType for u128 { + fn as_type() -> TypeKind { + TypeKind::U128 + } +} + +impl CType for i8 { + fn as_type() -> TypeKind { + TypeKind::I8 + } +} + +impl CType for i16 { + fn as_type() -> TypeKind { + TypeKind::I16 + } +} + +impl CType for i32 { + fn as_type() -> TypeKind { + TypeKind::I32 + } +} + +impl CType for i64 { + fn as_type() -> TypeKind { + TypeKind::I64 + } +} + +impl CType for i128 { + fn as_type() -> TypeKind { + TypeKind::I128 + } +} + +impl CType for f32 { + fn as_type() -> TypeKind { + TypeKind::F32 + } +} + +impl CType for f64 { + fn as_type() -> TypeKind { + TypeKind::F64 + } +} + +/// Something that can be used to indicate the type of arguments for a [`FunctionType`]. +pub trait ArgumentType { + /// The list of argument types that this [`ArgumentType`] represents. + fn as_types() -> Vec; +} + +impl ArgumentType for T { + fn as_types() -> Vec { + vec![T::as_type()] + } +} + +impl ArgumentType for (T1,) { + fn as_types() -> Vec { + vec![T1::as_type()] + } +} + +impl ArgumentType for (T1, T2) { + fn as_types() -> Vec { + vec![T1::as_type(), T2::as_type()] + } +} + +impl ArgumentType for (T1, T2, T3) { + fn as_types() -> Vec { + vec![T1::as_type(), T2::as_type(), T3::as_type()] + } +} + +impl ArgumentType + for (T1, T2, T3, T4) +{ + fn as_types() -> Vec { + vec![T1::as_type(), T2::as_type(), T3::as_type(), T4::as_type()] + } +} + +impl ArgumentType + for (T1, T2, T3, T4, T5) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + ] + } +} + +impl + ArgumentType for (T1, T2, T3, T4, T5, T6) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + T6::as_type(), + ] + } +} + +impl< + T1: CType, + T2: CType, + T3: CType, + T4: CType, + T5: CType, + T6: CType, + T7: CType, + > ArgumentType for (T1, T2, T3, T4, T5, T6, T7) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + T6::as_type(), + T7::as_type(), + ] + } +} + +impl< + T1: CType, + T2: CType, + T3: CType, + T4: CType, + T5: CType, + T6: CType, + T7: CType, + T8: CType, + > ArgumentType for (T1, T2, T3, T4, T5, T6, T7, T8) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + T6::as_type(), + T7::as_type(), + T8::as_type(), + ] + } +} + +impl< + T1: CType, + T2: CType, + T3: CType, + T4: CType, + T5: CType, + T6: CType, + T7: CType, + T8: CType, + T9: CType, + > ArgumentType for (T1, T2, T3, T4, T5, T6, T7, T8, T9) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + T6::as_type(), + T7::as_type(), + T8::as_type(), + T9::as_type(), + ] + } +} + +impl< + T1: CType, + T2: CType, + T3: CType, + T4: CType, + T5: CType, + T6: CType, + T7: CType, + T8: CType, + T9: CType, + T10: CType, + > ArgumentType for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + T6::as_type(), + T7::as_type(), + T8::as_type(), + T9::as_type(), + T10::as_type(), + ] + } +} + +impl< + T1: CType, + T2: CType, + T3: CType, + T4: CType, + T5: CType, + T6: CType, + T7: CType, + T8: CType, + T9: CType, + T10: CType, + T11: CType, + > ArgumentType for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + T6::as_type(), + T7::as_type(), + T8::as_type(), + T9::as_type(), + T10::as_type(), + T11::as_type(), + ] + } +} + +impl< + T1: CType, + T2: CType, + T3: CType, + T4: CType, + T5: CType, + T6: CType, + T7: CType, + T8: CType, + T9: CType, + T10: CType, + T11: CType, + T12: CType, + > ArgumentType for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) +{ + fn as_types() -> Vec { + vec![ + T1::as_type(), + T2::as_type(), + T3::as_type(), + T4::as_type(), + T5::as_type(), + T6::as_type(), + T7::as_type(), + T8::as_type(), + T9::as_type(), + T10::as_type(), + T11::as_type(), + T12::as_type(), + ] + } +} + +/// The type of a C function. +#[derive(Debug, Clone)] +pub struct FunctionType { + /// The return type of the function. + pub(crate) return_type: TypeKind, + + /// The type of the arguments. + pub(crate) argument_types: Vec, +} + +impl FunctionType { + /// Create a new function type with the given `ReturnType` and `ArgumentTypes`. + pub fn new() -> Self { + Self::from(ReturnType::as_type(), ArgumentTypes::as_types()) + } + + /// Create a new function type from the given `return_type` and `argument_types`. + /// If `argument_types` is a vector with a single [`TypeKind::Void`] item, + /// the function type will have an empty list of arguments, following the + /// C standard to describe functions which don't take any arguments: `function(void)`. + pub fn from(return_type: TypeKind, argument_types: Vec) -> Self { + let is_void_only_arguments = + argument_types.len() == 1 && argument_types[0] == TypeKind::Void; + Self { + return_type, + argument_types: if is_void_only_arguments { + Vec::new() + } else { + argument_types + }, + } + } +} diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index 448bcd22ea..b8d99a78a5 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -9,6 +9,7 @@ use super::ty::TypeKind; use crate::callbacks::{ItemInfo, ItemKind}; use crate::clang::{self, ABIKind, Attribute}; use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; +use cexpr::token::{Kind, Token}; use clang_sys::{self, CXCallingConv}; use quote::TokenStreamExt; @@ -24,6 +25,8 @@ pub(crate) enum FunctionKind { Function, /// A method of some kind. Method(MethodKind), + /// A functional macro. + Macro, } impl FunctionKind { @@ -725,6 +728,94 @@ impl ClangSubItemParser for Function { context: &mut BindgenContext, ) -> Result, ParseError> { use clang_sys::*; + if cursor.kind() == CXCursor_MacroDefinition { + if let Some(function_type) = context + .options() + .macro_functions + .get(&cursor.spelling()) + .cloned() + { + // Extract the macro argument names from the tokens. + let macro_tokens = cursor.cexpr_tokens(); + let argument_names = if macro_tokens.get(1) != + Some(&Token::from((Kind::Punctuation, b"(".as_slice()))) + { + Vec::new() + } else { + let mut argument_names = Vec::new(); + let mut token_iterator = macro_tokens.into_iter().skip(2); // Skip macro name and open parentheses. + while let Some(token) = token_iterator.next() { + if token.kind != Kind::Identifier { + break; + } + if let Ok(name) = String::from_utf8(token.raw.to_vec()) + { + argument_names.push(name); + } + if let Some(token) = token_iterator.next() { + if token.kind != Kind::Punctuation || + *token.raw != *b",".as_slice() + { + break; + } + } else { + break; + }; + } + argument_names + }; + + // Construct a [Function] for the macro. + let name = cursor.spelling(); + let mangled_name = None; + let link_name = context.options().last_callback(|callbacks| { + callbacks.generated_link_name_override(ItemInfo { + name: name.as_str(), + kind: ItemKind::Function, + }) + }); + let signature = Item::builtin_type( + TypeKind::Function(FunctionSig { + name: name.clone(), + return_type: Item::builtin_type( + function_type.return_type.clone().into(), + false, + context, + ), + argument_types: function_type + .argument_types + .iter() + .enumerate() + .map(|(index, kind)| { + ( + argument_names.get(index).cloned(), + Item::builtin_type( + kind.clone().into(), + false, + context, + ), + ) + }) + .collect(), + is_variadic: false, + is_divergent: false, + must_use: false, + abi: ClangAbi::Known(Abi::C), + }), + true, + context, + ); + let function = Self::new( + name, + mangled_name, + link_name, + signature, + FunctionKind::Macro, + Linkage::External, + ); + return Ok(ParseResult::New(function, Some(cursor))); + } + } let kind = match FunctionKind::from_cursor(&cursor) { None => return Err(ParseError::Continue), diff --git a/bindgen/ir/item.rs b/bindgen/ir/item.rs index 2941eb81e2..be5c8e23cc 100644 --- a/bindgen/ir/item.rs +++ b/bindgen/ir/item.rs @@ -1032,6 +1032,7 @@ impl Item { FunctionKind::Method(MethodKind::Virtual { .. }) => { cc.methods() } + FunctionKind::Macro => true, }, } } @@ -1322,6 +1323,7 @@ impl Item { TypeKind::Void | TypeKind::Int(..) | TypeKind::Pointer(..) | + TypeKind::Function(..) | TypeKind::Float(..) => {} _ => panic!("Unsupported builtin type"), } diff --git a/bindgen/lib.rs b/bindgen/lib.rs index c2df8a8a1f..5d53775122 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -40,6 +40,7 @@ mod options; mod time; pub mod callbacks; +pub mod function_types; mod clang; #[cfg(feature = "experimental")] diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 6b5b4d8cbc..91e9b1422d 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -11,6 +11,7 @@ use crate::codegen::{ }; use crate::deps::DepfileSpec; use crate::features::{RustFeatures, RustTarget}; +use crate::function_types::{FunctionType, TypeKind}; use crate::regex_set::RegexSet; use crate::Abi; use crate::Builder; @@ -2040,27 +2041,84 @@ options! { }, as_args: "--wrap-static-fns-suffix", }, - /// The path of the file where the wrappers for `static` functions will be emitted. - wrap_static_fns_path: Option { + /// The path of the file where generated native code will be emitted. + native_code_generation_path: Option { methods: { #[cfg(feature = "experimental")] - /// Set the path for the source code file that would be created if any wrapper - /// functions must be generated due to the presence of `static` functions. + /// Set the path for the source code file that would be created if any code + /// must be generated. /// /// `bindgen` will automatically add the right extension to the header and source code /// files. /// /// This option only comes into effect if `true` is passed to the - /// [`Builder::wrap_static_fns`] method. + /// [`Builder::wrap_static_fns`] method or if a function macro is defined + /// via [`Builder::macro_function`]. /// /// The default path is `temp_dir/bindgen/extern`, where `temp_dir` is the path /// returned by [`std::env::temp_dir`] . - pub fn wrap_static_fns_path>(mut self, path: T) -> Self { - self.options.wrap_static_fns_path = Some(path.as_ref().to_owned()); + pub fn native_code_generation_path>(mut self, path: T) -> Self { + self.options.native_code_generation_path = Some(path.as_ref().to_owned()); + self + } + }, + as_args: "--native-code-generation-path", + }, + /// A mapping of names to function types of registered functional macros + /// for which a wrapping function should be generated. + macro_functions: HashMap { + methods: { + /// Register a new functional macro with the given `name` for which a wrapper function will be generated. + /// The `function_type` specifies the concrete type as which the macro will be called. + /// + /// Example: + /// ```rust,ignore + /// Builder::default() + /// .macro_function("MACRO_WITH_NO_ARGUMENTS", FunctionType::new::<(), ()>()) + /// .macro_function("MACRO_WITH_ONE_ARGUMENT_AND_RETURN_VALUE", FunctionType::new::()) + /// .macro_function("MACRO_WITH_TWO_ARGUMENTS_AND_RETURN_VALUE", FunctionType::new::()) + /// ``` + pub fn macro_function>(mut self, name: T, function_type: FunctionType) -> Self { + let name = name.into(); + if self.options.macro_functions.insert(name.clone(), function_type).is_some() { + panic!("Duplicate definition for macro function {}", name); + } self } }, - as_args: "--wrap-static-fns-path", + as_args: |macro_functions, args| { + /// Serialize the `typ` into a format that will be accepted as a bindgen argument. + fn serialize_type(typ: &TypeKind) -> &'static str { + match typ { + TypeKind::Void => "void", + TypeKind::Bool => "bool", + TypeKind::Char => "char", + TypeKind::Int => "int", + TypeKind::Long => "long", + TypeKind::U8 => "u8", + TypeKind::U16 => "u16", + TypeKind::U32 => "u32", + TypeKind::U64 => "u64", + TypeKind::U128 => "u128", + TypeKind::I8 => "i8", + TypeKind::I16 => "i16", + TypeKind::I32 => "i32", + TypeKind::I64 => "i64", + TypeKind::I128 => "i128", + TypeKind::F32 => "f32", + TypeKind::F64 => "f64", + } + } + + for (name, function_type) in macro_functions.iter() { + args.push("--macro-function".to_string()); + args.push(format!( + "{return_type} {name}({arguments})", + return_type = serialize_type(&function_type.return_type), + arguments = function_type.argument_types.iter().map(serialize_type).collect::>().join(", "), + )); + } + }, }, /// Default visibility of fields. default_visibility: FieldVisibilityKind { From b43c620e1d443784060388d74f898a6dd1209094 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 7 Jul 2023 15:22:02 +0200 Subject: [PATCH 2/8] Add tests for macro wrap function generation --- .../expectations/tests/function_macros.rs | 22 +++++++++ .../tests/generated/function_macros.c | 11 +++++ .../tests/generated/wrap_static_fns.c | 2 + bindgen-tests/tests/headers/function_macros.h | 15 ++++++ bindgen-tests/tests/tests.rs | 49 ++++++++++++++++++- 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 bindgen-tests/tests/expectations/tests/function_macros.rs create mode 100644 bindgen-tests/tests/expectations/tests/generated/function_macros.c create mode 100644 bindgen-tests/tests/headers/function_macros.h diff --git a/bindgen-tests/tests/expectations/tests/function_macros.rs b/bindgen-tests/tests/expectations/tests/function_macros.rs new file mode 100644 index 0000000000..4ba60f0704 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/function_macros.rs @@ -0,0 +1,22 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +extern "C" { + pub fn SIMPLE(); +} +extern "C" { + pub fn INDIRECT_SIMPLE(); +} +extern "C" { + pub fn COMPLEX(x: u32) -> f32; +} +extern "C" { + pub fn INDIRECT_COMPLEX(x: u32) -> f32; +} +extern "C" { + pub fn CONDITIONAL_COMPLEX(condition: bool, x: u32) -> f32; +} +extern "C" { + pub fn simple(); +} +extern "C" { + pub fn complex(arg1: ::std::os::raw::c_int) -> f32; +} diff --git a/bindgen-tests/tests/expectations/tests/generated/function_macros.c b/bindgen-tests/tests/expectations/tests/generated/function_macros.c new file mode 100644 index 0000000000..2b2675df79 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/generated/function_macros.c @@ -0,0 +1,11 @@ +#include +#include +#include "tests/headers/function_macros.h" + +// Macro function wrappers + +void SIMPLE__macro(void) { SIMPLE; } +void INDIRECT_SIMPLE__macro(void) { INDIRECT_SIMPLE; } +float COMPLEX__macro(uint32_t x) { return COMPLEX(x); } +float INDIRECT_COMPLEX__macro(uint32_t x) { return INDIRECT_COMPLEX(x); } +float CONDITIONAL_COMPLEX__macro(bool condition, uint32_t x) { return CONDITIONAL_COMPLEX(condition, x); } diff --git a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.c b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.c index a8fd5045d2..fdf92ea1f2 100644 --- a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.c +++ b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.c @@ -1,3 +1,5 @@ +#include +#include #include "tests/headers/wrap-static-fns.h" // Static wrappers diff --git a/bindgen-tests/tests/headers/function_macros.h b/bindgen-tests/tests/headers/function_macros.h new file mode 100644 index 0000000000..f47212f7dd --- /dev/null +++ b/bindgen-tests/tests/headers/function_macros.h @@ -0,0 +1,15 @@ +// bindgen-flags: --macro-function "SIMPLE" --macro-function "INDIRECT_SIMPLE" --macro-function "f32 COMPLEX(u32)" --macro-function "f32 INDIRECT_COMPLEX(u32)" --macro-function "f32 CONDITIONAL_COMPLEX(bool, u32)" + +void simple(void); + +#define SIMPLE simple() +#define INDIRECT_SIMPLE SIMPLE + +float complex(int); + +#define COMPLEX(x) complex(x) +#define INDIRECT_COMPLEX(x) COMPLEX(x) + +#define CONDITIONAL_COMPLEX(condition, x) ((condition) ? COMPLEX(x) : 0) + +#define SIMPLE_NOT_CONFIGURED simple() diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs index 25649421fc..d272fddad7 100644 --- a/bindgen-tests/tests/tests.rs +++ b/bindgen-tests/tests/tests.rs @@ -4,7 +4,7 @@ extern crate clap; extern crate env_logger; extern crate shlex; -use bindgen::{clang_version, Builder}; +use bindgen::{clang_version, function_types::FunctionType, Builder}; use owo_colors::{OwoColorize, Style}; use similar::{ChangeTag, TextDiff}; use std::env; @@ -711,3 +711,50 @@ fn test_wrap_static_fns() { .unwrap(); } } + +#[test] +fn test_function_macros() { + if env::current_dir().unwrap().ends_with("rust-bindgen") { + env::set_current_dir(Path::new("bindgen-tests")).unwrap(); + } + if env::var("OUT_DIR").is_err() { + env::set_var("OUT_DIR", PathBuf::from("target/out")); + } + let expect_path = PathBuf::from("tests/expectations/tests/generated") + .join("function_macros"); + println!("In path is ::: {}", expect_path.display()); + + let generated_path = + PathBuf::from(env::var("OUT_DIR").unwrap()).join("function_macros"); + println!("Out path is ::: {}", generated_path.display()); + + let _bindings = Builder::default() + .header("tests/headers/function_macros.h") + .macro_function("SIMPLE", FunctionType::new::<(), ()>()) + .macro_function("INDIRECT_SIMPLE", FunctionType::new::<(), ()>()) + .macro_function("COMPLEX", FunctionType::new::()) + .macro_function("INDIRECT_COMPLEX", FunctionType::new::()) + .macro_function( + "CONDITIONAL_COMPLEX", + FunctionType::new::(), + ) + .native_code_generation_path(generated_path.display().to_string()) + .generate() + .expect("Failed to generate bindings"); + + let expected_c = fs::read_to_string(expect_path.with_extension("c")) + .expect("Could not read generated function_macros.c"); + + let actual_c = fs::read_to_string(generated_path.with_extension("c")) + .expect("Could not read actual function_macros.c"); + + if expected_c != actual_c { + error_diff_mismatch( + &actual_c, + &expected_c, + None, + &expect_path.with_extension("c"), + ) + .unwrap(); + } +} From 5ca0f2685fe956d0dfff1a52fe17821aa95532d4 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 7 Jul 2023 15:26:48 +0200 Subject: [PATCH 3/8] Add changes to Changelog --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95b7ac2966..d763dcc3ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -262,6 +262,9 @@ for every input header file by default. - Added the `CargoCallbacks::rerun_on_header_files` method to configure whether a cargo-rerun line should be emitted for every input header file. +- Added `--macro-function` argument and corresponding `macro_function` builder function. + These allow defining macros for which a wrapper function will be generated, + which allows to call these macros from Rust. ## Changed - The `--wrap-static-fns` feature was updated so function types that has no argument use `void` as its sole argument. @@ -270,6 +273,8 @@ `CargoCallbacks` constant was added to mitigate the breaking nature of this change. This constant has been marked as deprecated and users will have to use the new `CargoCallbacks::new` method in the future. +- Renamed `--wrap-static-fns-path` argument to `--native-code-generation-path` and the + corresponding `wrap_static_fns_path` builder function to `native_code_generation_path`. ## Removed ## Fixed - Allow compiling `bindgen-cli` with a static libclang. @@ -328,7 +333,7 @@ This version was skipped due to some problems on the release workflow. * The `--wrap-static-fns` option can now wrap `va_list` functions as variadic functions with the experimental `ParseCallbacks::wrap_as_variadic_fn` method. * Add target mappings for riscv32imc and riscv32imac. -* Add the `ParseCallbacks::field_visibility` method to modify field visibility. +* Add the `ParseCallbacks::field_visibility` method to modify field visibility. ## Changed @@ -352,7 +357,7 @@ This version was skipped due to some problems on the release workflow. * Compute visibility of bitfield unit based on actual field visibility: A bitfield unit field and its related functions now have their visibility determined based on the most private between the default visibility and the - actual visibility of the bitfields within the unit. + actual visibility of the bitfields within the unit. ## Removed * Remove redundant Cargo features, which were all implicit: @@ -408,7 +413,7 @@ This version was skipped due to some problems on the release workflow. types. (#2463) * The `Builder::rustfmt_bindings` methods and the `--no-rustfmt-bindings` flag are now deprecated in favor of the formatter API. (#2453) - + ## Removed * The following deprecated flags were removed: `--use-msvc-mangling`, `--rustfmt-bindings` and `--size_t-is-usize`. (#2408) From a5463fc1e03fc734a3a7876660a146f83c9b3846 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Tue, 11 Jul 2023 15:17:49 +0200 Subject: [PATCH 4/8] Make sure macro wrapper functions have the correct link name --- bindgen-tests/tests/expectations/tests/function_macros.rs | 5 +++++ bindgen/codegen/mod.rs | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bindgen-tests/tests/expectations/tests/function_macros.rs b/bindgen-tests/tests/expectations/tests/function_macros.rs index 4ba60f0704..1d1217ca81 100644 --- a/bindgen-tests/tests/expectations/tests/function_macros.rs +++ b/bindgen-tests/tests/expectations/tests/function_macros.rs @@ -1,17 +1,22 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] extern "C" { + #[link_name = "SIMPLE__macro"] pub fn SIMPLE(); } extern "C" { + #[link_name = "INDIRECT_SIMPLE__macro"] pub fn INDIRECT_SIMPLE(); } extern "C" { + #[link_name = "COMPLEX__macro"] pub fn COMPLEX(x: u32) -> f32; } extern "C" { + #[link_name = "INDIRECT_COMPLEX__macro"] pub fn INDIRECT_COMPLEX(x: u32) -> f32; } extern "C" { + #[link_name = "CONDITIONAL_COMPLEX__macro"] pub fn CONDITIONAL_COMPLEX(condition: bool, x: u32) -> f32; } extern "C" { diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f247895932..a009a6f1e6 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4125,7 +4125,8 @@ impl CodeGenerator for Function { debug!("::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); - if self.kind() == FunctionKind::Macro { + let is_function_macro = self.kind() == FunctionKind::Macro; + if is_function_macro { result .items_to_serialize .push(SerializableItem::MacroFunction(item.id())); @@ -4275,6 +4276,9 @@ impl CodeGenerator for Function { if should_wrap { let name = canonical_name.clone() + ctx.wrap_static_fns_suffix(); attributes.push(attributes::link_name::(&name)); + } else if is_function_macro && !has_link_name_attr { + let name = canonical_name.clone() + "__macro"; + attributes.push(attributes::link_name::(&name)); } let wrap_as_variadic = if should_wrap && !signature.is_variadic() { From 3b1372cc0b63b0b7b0bd3e610380c0b0290a468d Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 6 Oct 2023 13:10:02 +0200 Subject: [PATCH 5/8] Make the macro-function argument experimental --- bindgen-cli/options.rs | 2 +- bindgen-tests/tests/headers/function_macros.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindgen-cli/options.rs b/bindgen-cli/options.rs index acb5909f7b..049861b4f5 100644 --- a/bindgen-cli/options.rs +++ b/bindgen-cli/options.rs @@ -425,7 +425,7 @@ struct BindgenCommand { wrap_static_fns_suffix: Option, /// Create a wrapper function for the macro. The MACRO value must be of the shape /// `[] [()]`. - #[arg(long, value_name = "MACRO")] + #[arg(long, requires = "experimental", value_name = "MACRO")] macro_function: Option>, /// Set the default VISIBILITY of fields, including bitfields and accessor methods for /// bitfields. This flag is ignored if the `--respect-cxx-access-specs` flag is used. diff --git a/bindgen-tests/tests/headers/function_macros.h b/bindgen-tests/tests/headers/function_macros.h index f47212f7dd..82aaca079b 100644 --- a/bindgen-tests/tests/headers/function_macros.h +++ b/bindgen-tests/tests/headers/function_macros.h @@ -1,4 +1,4 @@ -// bindgen-flags: --macro-function "SIMPLE" --macro-function "INDIRECT_SIMPLE" --macro-function "f32 COMPLEX(u32)" --macro-function "f32 INDIRECT_COMPLEX(u32)" --macro-function "f32 CONDITIONAL_COMPLEX(bool, u32)" +// bindgen-flags: --experimental --macro-function "SIMPLE" --macro-function "INDIRECT_SIMPLE" --macro-function "f32 COMPLEX(u32)" --macro-function "f32 INDIRECT_COMPLEX(u32)" --macro-function "f32 CONDITIONAL_COMPLEX(bool, u32)" void simple(void); From 54d1ad7067fd271fd311784c556e5d30ecca1354 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 6 Oct 2023 13:21:32 +0200 Subject: [PATCH 6/8] Rename native_code_generation_path to wrapper_code_generation_path --- CHANGELOG.md | 4 ++-- bindgen-cli/options.rs | 10 +++++----- bindgen-integration/build.rs | 2 +- bindgen-tests/tests/tests.rs | 4 ++-- bindgen/codegen/mod.rs | 2 +- bindgen/options/mod.rs | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d763dcc3ab..4976cc3bad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -273,8 +273,8 @@ `CargoCallbacks` constant was added to mitigate the breaking nature of this change. This constant has been marked as deprecated and users will have to use the new `CargoCallbacks::new` method in the future. -- Renamed `--wrap-static-fns-path` argument to `--native-code-generation-path` and the - corresponding `wrap_static_fns_path` builder function to `native_code_generation_path`. +- Renamed `--wrap-static-fns-path` argument to `-wrapper-code-generation-path` and the + corresponding `wrap_static_fns_path` builder function to `wrapper_code_generation_path`. ## Removed ## Fixed - Allow compiling `bindgen-cli` with a static libclang. diff --git a/bindgen-cli/options.rs b/bindgen-cli/options.rs index 049861b4f5..86372079b4 100644 --- a/bindgen-cli/options.rs +++ b/bindgen-cli/options.rs @@ -416,9 +416,9 @@ struct BindgenCommand { /// Generate wrappers for `static` and `static inline` functions. #[arg(long, requires = "experimental")] wrap_static_fns: bool, - /// Sets the path of the file where generated native code will be emitted. + /// Sets the path of the file where generated code for wrapper functions will be emitted. #[arg(long, requires = "experimental", value_name = "PATH")] - native_code_generation_path: Option, + wrapper_code_generation_path: Option, /// Sets the SUFFIX added to the extern wrapper functions generated for `static` and `static /// inline` functions. #[arg(long, requires = "experimental", value_name = "SUFFIX")] @@ -563,7 +563,7 @@ where with_derive_custom_enum, with_derive_custom_union, wrap_static_fns, - native_code_generation_path, + wrapper_code_generation_path, wrap_static_fns_suffix, macro_function, default_visibility, @@ -1107,8 +1107,8 @@ where builder = builder.wrap_static_fns(true); } - if let Some(path) = native_code_generation_path { - builder = builder.native_code_generation_path(path); + if let Some(path) = wrapper_code_generation_path { + builder = builder.wrapper_code_generation_path(path); } if let Some(suffix) = wrap_static_fns_suffix { diff --git a/bindgen-integration/build.rs b/bindgen-integration/build.rs index 971959cd9b..2fb9ad2ec4 100644 --- a/bindgen-integration/build.rs +++ b/bindgen-integration/build.rs @@ -234,7 +234,7 @@ fn setup_wrap_static_fns_test() { .parse_callbacks(Box::new(CargoCallbacks)) .parse_callbacks(Box::new(WrappedVaListCallback)) .wrap_static_fns(true) - .native_code_generation_path( + .wrapper_code_generation_path( out_path.join("wrap_static_fns").display().to_string(), ) .clang_arg("-DUSE_VA_HEADER") diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs index d272fddad7..d6e24279b7 100644 --- a/bindgen-tests/tests/tests.rs +++ b/bindgen-tests/tests/tests.rs @@ -690,7 +690,7 @@ fn test_wrap_static_fns() { let _bindings = Builder::default() .header("tests/headers/wrap-static-fns.h") .wrap_static_fns(true) - .native_code_generation_path(generated_path.display().to_string()) + .wrapper_code_generation_path(generated_path.display().to_string()) .parse_callbacks(Box::new(parse_callbacks::WrapAsVariadicFn)) .generate() .expect("Failed to generate bindings"); @@ -738,7 +738,7 @@ fn test_function_macros() { "CONDITIONAL_COMPLEX", FunctionType::new::(), ) - .native_code_generation_path(generated_path.display().to_string()) + .wrapper_code_generation_path(generated_path.display().to_string()) .generate() .expect("Failed to generate bindings"); diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index a009a6f1e6..7721c584e2 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4811,7 +4811,7 @@ pub(crate) mod utils { let path = context .options() - .native_code_generation_path + .wrapper_code_generation_path .as_ref() .map(PathBuf::from) .unwrap_or_else(|| { diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 91e9b1422d..841f9a558f 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -2042,7 +2042,7 @@ options! { as_args: "--wrap-static-fns-suffix", }, /// The path of the file where generated native code will be emitted. - native_code_generation_path: Option { + wrapper_code_generation_path: Option { methods: { #[cfg(feature = "experimental")] /// Set the path for the source code file that would be created if any code @@ -2057,12 +2057,12 @@ options! { /// /// The default path is `temp_dir/bindgen/extern`, where `temp_dir` is the path /// returned by [`std::env::temp_dir`] . - pub fn native_code_generation_path>(mut self, path: T) -> Self { - self.options.native_code_generation_path = Some(path.as_ref().to_owned()); + pub fn wrapper_code_generation_path>(mut self, path: T) -> Self { + self.options.wrapper_code_generation_path = Some(path.as_ref().to_owned()); self } }, - as_args: "--native-code-generation-path", + as_args: "--wrapper-code-generation-path", }, /// A mapping of names to function types of registered functional macros /// for which a wrapping function should be generated. From 10a0ac7efd102ccd0743698c49c00bcde6e75e64 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 6 Oct 2023 16:56:49 +0200 Subject: [PATCH 7/8] Don't use let...else statement --- bindgen-cli/options.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bindgen-cli/options.rs b/bindgen-cli/options.rs index 86372079b4..930492954a 100644 --- a/bindgen-cli/options.rs +++ b/bindgen-cli/options.rs @@ -1122,11 +1122,8 @@ where { (macro_definition.as_str(), Vec::new()) } else { - let Some((left_side, arguments)) = - macro_definition.split_once('(') - else { - panic!("Invalid function macro definition: No '(' for ')' at end found"); - }; + let (left_side, arguments) = + macro_definition.split_once('(').expect("Invalid function macro definition: No '(' for ')' at end found"); let arguments = &arguments[..arguments.len() - 1]; if arguments.trim().is_empty() { // The empty argument list case `()`. From 26f18f511997e7ebdfba9e95fe825820c0787e9f Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 6 Oct 2023 17:09:35 +0200 Subject: [PATCH 8/8] Rename wrap_static_fns_suffix and use it for functional macros --- CHANGELOG.md | 4 +++- bindgen-cli/options.rs | 12 ++++++------ .../tests/expectations/tests/function_macros.rs | 10 +++++----- .../expectations/tests/generated/function_macros.c | 10 +++++----- bindgen/codegen/mod.rs | 7 ++----- bindgen/codegen/serialize.rs | 10 +--------- bindgen/ir/context.rs | 11 ++++++----- bindgen/lib.rs | 4 ++-- bindgen/options/mod.rs | 14 +++++++------- 9 files changed, 37 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4976cc3bad..00e94b97b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -273,8 +273,10 @@ `CargoCallbacks` constant was added to mitigate the breaking nature of this change. This constant has been marked as deprecated and users will have to use the new `CargoCallbacks::new` method in the future. -- Renamed `--wrap-static-fns-path` argument to `-wrapper-code-generation-path` and the +- Renamed `--wrap-static-fns-path` argument to `--wrapper-code-generation-path` and the corresponding `wrap_static_fns_path` builder function to `wrapper_code_generation_path`. +- Renamed `--wrap-static-fns-suffix` argument to `--wrapper-function-suffix` and the + corresponding `wrap_static_fns_suffix` builder function to `wrapper_function_suffix`. ## Removed ## Fixed - Allow compiling `bindgen-cli` with a static libclang. diff --git a/bindgen-cli/options.rs b/bindgen-cli/options.rs index 930492954a..c359f8d7d0 100644 --- a/bindgen-cli/options.rs +++ b/bindgen-cli/options.rs @@ -419,10 +419,10 @@ struct BindgenCommand { /// Sets the path of the file where generated code for wrapper functions will be emitted. #[arg(long, requires = "experimental", value_name = "PATH")] wrapper_code_generation_path: Option, - /// Sets the SUFFIX added to the extern wrapper functions generated for `static` and `static - /// inline` functions. + /// Sets the SUFFIX added to the wrapper functions generated for `static` and `static inline` + /// functions and functional macros. #[arg(long, requires = "experimental", value_name = "SUFFIX")] - wrap_static_fns_suffix: Option, + wrapper_function_suffix: Option, /// Create a wrapper function for the macro. The MACRO value must be of the shape /// `[] [()]`. #[arg(long, requires = "experimental", value_name = "MACRO")] @@ -564,7 +564,7 @@ where with_derive_custom_union, wrap_static_fns, wrapper_code_generation_path, - wrap_static_fns_suffix, + wrapper_function_suffix, macro_function, default_visibility, emit_diagnostics, @@ -1111,8 +1111,8 @@ where builder = builder.wrapper_code_generation_path(path); } - if let Some(suffix) = wrap_static_fns_suffix { - builder = builder.wrap_static_fns_suffix(suffix); + if let Some(suffix) = wrapper_function_suffix { + builder = builder.wrapper_function_suffix(suffix); } if let Some(macro_functions) = macro_function { diff --git a/bindgen-tests/tests/expectations/tests/function_macros.rs b/bindgen-tests/tests/expectations/tests/function_macros.rs index 1d1217ca81..fb0a1c225d 100644 --- a/bindgen-tests/tests/expectations/tests/function_macros.rs +++ b/bindgen-tests/tests/expectations/tests/function_macros.rs @@ -1,22 +1,22 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] extern "C" { - #[link_name = "SIMPLE__macro"] + #[link_name = "SIMPLE__extern"] pub fn SIMPLE(); } extern "C" { - #[link_name = "INDIRECT_SIMPLE__macro"] + #[link_name = "INDIRECT_SIMPLE__extern"] pub fn INDIRECT_SIMPLE(); } extern "C" { - #[link_name = "COMPLEX__macro"] + #[link_name = "COMPLEX__extern"] pub fn COMPLEX(x: u32) -> f32; } extern "C" { - #[link_name = "INDIRECT_COMPLEX__macro"] + #[link_name = "INDIRECT_COMPLEX__extern"] pub fn INDIRECT_COMPLEX(x: u32) -> f32; } extern "C" { - #[link_name = "CONDITIONAL_COMPLEX__macro"] + #[link_name = "CONDITIONAL_COMPLEX__extern"] pub fn CONDITIONAL_COMPLEX(condition: bool, x: u32) -> f32; } extern "C" { diff --git a/bindgen-tests/tests/expectations/tests/generated/function_macros.c b/bindgen-tests/tests/expectations/tests/generated/function_macros.c index 2b2675df79..1b21176a6f 100644 --- a/bindgen-tests/tests/expectations/tests/generated/function_macros.c +++ b/bindgen-tests/tests/expectations/tests/generated/function_macros.c @@ -4,8 +4,8 @@ // Macro function wrappers -void SIMPLE__macro(void) { SIMPLE; } -void INDIRECT_SIMPLE__macro(void) { INDIRECT_SIMPLE; } -float COMPLEX__macro(uint32_t x) { return COMPLEX(x); } -float INDIRECT_COMPLEX__macro(uint32_t x) { return INDIRECT_COMPLEX(x); } -float CONDITIONAL_COMPLEX__macro(bool condition, uint32_t x) { return CONDITIONAL_COMPLEX(condition, x); } +void SIMPLE__extern(void) { SIMPLE; } +void INDIRECT_SIMPLE__extern(void) { INDIRECT_SIMPLE; } +float COMPLEX__extern(uint32_t x) { return COMPLEX(x); } +float INDIRECT_COMPLEX__extern(uint32_t x) { return INDIRECT_COMPLEX(x); } +float CONDITIONAL_COMPLEX__extern(bool condition, uint32_t x) { return CONDITIONAL_COMPLEX(condition, x); } diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 7721c584e2..554f6f3bf8 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4273,11 +4273,8 @@ impl CodeGenerator for Function { let should_wrap = is_internal && ctx.options().wrap_static_fns && !has_link_name_attr; - if should_wrap { - let name = canonical_name.clone() + ctx.wrap_static_fns_suffix(); - attributes.push(attributes::link_name::(&name)); - } else if is_function_macro && !has_link_name_attr { - let name = canonical_name.clone() + "__macro"; + if should_wrap || (is_function_macro && !has_link_name_attr) { + let name = canonical_name.clone() + ctx.wrapper_function_suffix(); attributes.push(attributes::link_name::(&name)); } diff --git a/bindgen/codegen/serialize.rs b/bindgen/codegen/serialize.rs index 9c8c99cb54..6588c2ebf8 100644 --- a/bindgen/codegen/serialize.rs +++ b/bindgen/codegen/serialize.rs @@ -119,15 +119,7 @@ impl<'a> CSerialize<'a> for Function { self.kind() != FunctionKind::Macro || !args.is_empty(); // The name used for the wrapper self. - let wrap_name = format!( - "{}{}", - name, - if self.kind() == FunctionKind::Macro { - "__macro" - } else { - ctx.wrap_static_fns_suffix() - } - ); + let wrap_name = format!("{name}{}", ctx.wrapper_function_suffix()); // The function's return type let (ret_item, ret_ty) = { diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index f13f34abcd..87e2d6000b 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -2863,13 +2863,14 @@ If you encounter an error missing from this list, please file an issue or a PR!" } } - /// Get the suffix to be added to `static` functions if the `--wrap-static-fns` option is - /// enabled. - pub(crate) fn wrap_static_fns_suffix(&self) -> &str { + /// Get the suffix to be added to generated wrapper functions like + /// `static` functions if the `--wrap-static-fns` option is enabled + /// or functional macros. + pub(crate) fn wrapper_function_suffix(&self) -> &str { self.options() - .wrap_static_fns_suffix + .wrapper_function_suffix .as_deref() - .unwrap_or(crate::DEFAULT_NON_EXTERN_FNS_SUFFIX) + .unwrap_or(crate::DEFAULT_WRAPPER_FUNCTION_SUFFIX) } } diff --git a/bindgen/lib.rs b/bindgen/lib.rs index 5d53775122..2caae14428 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -85,8 +85,8 @@ type HashSet = rustc_hash::FxHashSet; /// Default prefix for the anon fields. pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_"; - -const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern"; +/// Default suffix for wrapper functions. +const DEFAULT_WRAPPER_FUNCTION_SUFFIX: &str = "__extern"; fn file_is_cpp(name_file: &str) -> bool { name_file.ends_with(".hpp") || diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 841f9a558f..2fc3c78465 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -2024,22 +2024,22 @@ options! { }, as_args: "--wrap-static-fns", }, - /// The suffix to be added to the function wrappers for `static` functions. - wrap_static_fns_suffix: Option { + /// The suffix to be added to the function wrappers for `static` functions and functional macros. + wrapper_function_suffix: Option { methods: { #[cfg(feature = "experimental")] - /// Set the suffix added to the wrappers for `static` functions. + /// Set the suffix added to the wrappers for `static` functions and functional macros. /// /// This option only comes into effect if `true` is passed to the - /// [`Builder::wrap_static_fns`] method. + /// [`Builder::wrap_static_fns`] method if a functional macro is defined. /// /// The default suffix is `__extern`. - pub fn wrap_static_fns_suffix>(mut self, suffix: T) -> Self { - self.options.wrap_static_fns_suffix = Some(suffix.as_ref().to_owned()); + pub fn wrapper_function_suffix>(mut self, suffix: T) -> Self { + self.options.wrapper_function_suffix = Some(suffix.as_ref().to_owned()); self } }, - as_args: "--wrap-static-fns-suffix", + as_args: "--wrapper-function-suffix", }, /// The path of the file where generated native code will be emitted. wrapper_code_generation_path: Option {