Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,15 @@ All function attributes are just local overrides for the same options found in t
* prefix
* postfix

## Generating Swift Bindings

In addition to parsing function names in C/C++ header files, the Swift compiler can make use of the `swift_name` attribute on functions to generate more idiomatic names for imported functions and methods.

This attribute is commonly used in Objective-C/C/C++ via the `NS_SWIFT_NAME` and `CF_SWIFT_NAME` macros.

Given configuration in the cbindgen.toml, `cbindgen` can generate these attributes for you by guessing an appropriate method signature based on the existing function name (and type, if it is a method in an `impl` block).

This is controlled by the `swift_name_macro` option in the cbindgen.toml.


## cbindgen.toml
Expand Down Expand Up @@ -545,6 +553,13 @@ args = "horizontal"
# default: nothing is emitted for must_use functions
must_use = "MUST_USE_FUNC"

# An optional string that, if present, will be used to generate Swift function
# and method signatures for generated functions, for example "CF_SWIFT_NAME".
# If no such macro is available in your toolchain, you can define one using the
# `header` option in cbindgen.toml
# default: no swift_name function attributes are generated
swift_name_macro = "CF_SWIFT_NAME"

# A rule to use to rename function argument names. The renaming assumes the input
# is the Rust standard snake_case, however it accepts all the different rename_args
# inputs. This means many options here are no-ops or redundant.
Expand Down
5 changes: 4 additions & 1 deletion src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,14 @@ pub struct FunctionConfig {
pub prefix: Option<String>,
/// Optional text to output after each function declaration
pub postfix: Option<String>,
/// The way to annotation this function as #[must_use].
/// The way to annotation this function as #[must_use]
pub must_use: Option<String>,
/// The style to layout the args
pub args: Layout,
/// The rename rule to apply to function args
pub rename_args: Option<RenameRule>,
/// An optional macro to use when generating Swift function name attributes
pub swift_name_macro: Option<String>,
}

impl Default for FunctionConfig {
Expand All @@ -295,6 +297,7 @@ impl Default for FunctionConfig {
must_use: None,
args: Layout::Auto,
rename_args: None,
swift_name_macro: None,
}
}
}
Expand Down
59 changes: 59 additions & 0 deletions src/bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,20 @@ use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Function {
pub path: Path,
pub self_type_path: Option<Path>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment saying something like "If we're a method, this will be Some, and the path will be the path of the type in the impl block" or such.

It'd be nice to make something more similar to what associated constants do, but it may be overkill for this.

pub ret: Type,
pub args: Vec<(String, Type)>,
pub extern_decl: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub attributes: Vec<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed now.

}

impl Function {
pub fn load(
path: Path,
self_type_path: Option<Path>,
sig: &syn::Signature,
extern_decl: bool,
attrs: &[syn::Attribute],
Expand All @@ -53,15 +56,52 @@ impl Function {

Ok(Function {
path,
self_type_path,
ret,
args,
extern_decl,
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
annotations: AnnotationSet::load(attrs)?,
documentation: Documentation::load(attrs),
attributes: Vec::new(),
})
}

pub fn swift_name(&self) -> String {
// If the symbol name starts with the type name, separate the two components with '.'
// so that Swift recognises the association between the method and the type
let (ref type_prefix, ref type_name) = if let Some(type_name) = &self.self_type_path {
let type_name = type_name.to_string();
if !self.path.name().starts_with(&type_name) {
return self.path.to_string();
}
(format!("{}.", type_name), type_name)
} else {
("".to_string(), "".to_string())
};

let item_name = self.path
.name()
.trim_start_matches(type_name)
.trim_start_matches('_');

let item_args = {
let mut items = vec![];
for (arg, _) in self.args.iter() {
match arg.as_str() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is match is just items.push(format!("{}:", arg.as_str()) right?

"self" => {
items.push("self:".to_string());
}
other => {
items.push(format!("{}:", other));
}
}
}
items.join("")
};
format!("{}{}({})", type_prefix, item_name, item_args)
}

pub fn path(&self) -> &Path {
&self.path
}
Expand Down Expand Up @@ -160,12 +200,22 @@ impl Source for Function {
}
}
cdecl::write_func(out, &func, false, void_prototype);

if !func.extern_decl {
if let Some(ref postfix) = postfix {
out.write(" ");
write!(out, "{}", postfix);
}
}

if let Some(ref swift_name_macro) = config.function.swift_name_macro {
let swift_name = func.swift_name();
if !swift_name.is_empty() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can never be empty, right?

out.write(" ");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, just put the space in the string literal below? These two comments apply to the following block as well.

write!(out, "{}({})", swift_name_macro, func.swift_name());
}
}

out.write(";");

condition.write_after(config, out);
Expand Down Expand Up @@ -203,6 +253,15 @@ impl Source for Function {
write!(out, "{}", postfix);
}
}

if let Some(ref swift_name_macro) = config.function.swift_name_macro {
let swift_name = func.swift_name();
if !swift_name.is_empty() {
out.write(" ");
write!(out, "{}({})", swift_name_macro, func.swift_name());
}
}

out.write(";");

condition.write_after(config, out);
Expand Down
124 changes: 85 additions & 39 deletions src/bindgen/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,24 @@ impl Parse {
if has_assoc_const {
impls_with_assoc_consts.push(item_impl);
}

if let syn::Type::Path(ref path) = *item_impl.self_ty {
if let Some(type_name) = path.path.get_ident() {
for method in item_impl.items.iter().filter_map(|item| match item {
syn::ImplItem::Method(method) => Some(method),
_ => None,
}) {
self.load_syn_method(
config,
binding_crate_name,
crate_name,
mod_cfg,
Path::new(type_name.to_string()),
method,
)
}
}
}
}
syn::Item::Macro(ref item) => {
self.load_builtin_macro(config, crate_name, mod_cfg, item)
Expand Down Expand Up @@ -523,7 +541,7 @@ impl Parse {
return;
}
let path = Path::new(function.sig.ident.to_string());
match Function::load(path, &function.sig, true, &function.attrs, mod_cfg) {
match Function::load(path, None, &function.sig, true, &function.attrs, mod_cfg) {
Ok(func) => {
info!("Take {}::{}.", crate_name, &function.sig.ident);

Expand All @@ -540,6 +558,29 @@ impl Parse {
}
}

/// Loads a `fn` declaration inside an `impl` block, if the type is a simple identifier
fn load_syn_method(
&mut self,
config: &Config,
binding_crate_name: &str,
crate_name: &str,
mod_cfg: Option<&Cfg>,
self_type: Path,
item: &syn::ImplItemMethod,
) {
self.load_fn_declaration(
config,
binding_crate_name,
crate_name,
mod_cfg,
item,
Some(self_type),
&item.sig,
&item.vis,
&item.attrs,
)
}

/// Loads a `fn` declaration
fn load_syn_fn(
&mut self,
Expand All @@ -548,65 +589,70 @@ impl Parse {
crate_name: &str,
mod_cfg: Option<&Cfg>,
item: &syn::ItemFn,
) {
self.load_fn_declaration(
config,
binding_crate_name,
crate_name,
mod_cfg,
item,
None,
&item.sig,
&item.vis,
&item.attrs,
);
}

fn load_fn_declaration(
&mut self,
config: &Config,
binding_crate_name: &str,
crate_name: &str,
mod_cfg: Option<&Cfg>,
named_symbol: &dyn SynItemFnHelpers,
self_type: Option<Path>,
sig: &syn::Signature,
vis: &syn::Visibility,
attrs: &[syn::Attribute],
) {
if !config
.parse
.should_generate_top_level_item(crate_name, binding_crate_name)
{
info!(
"Skip {}::{} - (fn's outside of the binding crate are not used).",
crate_name, &item.sig.ident
crate_name, &sig.ident
);
return;
}

if let syn::Visibility::Public(_) = item.vis {
if item.sig.abi.is_omitted() || item.sig.abi.is_c() {
if let Some(exported_name) = item.exported_name() {
if let syn::Visibility::Public(_) = vis {
if sig.abi.is_omitted() || sig.abi.is_c() {
if let Some(exported_name) = named_symbol.exported_name() {
let path = Path::new(exported_name);
match Function::load(path, &item.sig, false, &item.attrs, mod_cfg) {
match Function::load(path, self_type, &sig, false, &attrs, mod_cfg) {
Ok(func) => {
info!("Take {}::{}.", crate_name, &item.sig.ident);

info!("Take {}::{}.", crate_name, &sig.ident);
self.functions.push(func);
}
Err(msg) => {
error!(
"Cannot use fn {}::{} ({}).",
crate_name, &item.sig.ident, msg
);
error!("Cannot use fn {}::{} ({}).", crate_name, &sig.ident, msg);
}
}
return;
} else {
warn!(
"Skipping {}::{} - (not `no_mangle`, and has no `export_name` attribute)",
crate_name, &sig.ident
);
}
} else {
warn!(
"Skipping {}::{} - (not `extern \"C\"`",
crate_name, &sig.ident
);
}
}

// TODO
if let syn::Visibility::Public(_) = item.vis {
} else {
warn!("Skip {}::{} - (not `pub`).", crate_name, &item.sig.ident);
}

if !(item.sig.abi.is_omitted() || item.sig.abi.is_c()) {
warn!(
"Skip {}::{} - (wrong ABI - not `extern` or `extern \"C\"`).",
crate_name, &item.sig.ident
);
}

if item.exported_name().is_none() {
warn!(
"Skip {}::{} - (not `no_mangle`, and has no `export_name` attribute)",
crate_name, &item.sig.ident
);
}

if item.sig.abi.is_some() && !(item.sig.abi.is_omitted() || item.sig.abi.is_c()) {
warn!(
"Skip {}::{} - (non `extern \"C\"`).",
crate_name, &item.sig.ident
);
warn!("Skipping {}::{} - (not `pub`)", crate_name, &sig.ident);
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/bindgen/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ impl SynItemFnHelpers for syn::ItemFn {
}
}

impl SynItemFnHelpers for syn::ImplItemMethod {
fn exported_name(&self) -> Option<String> {
self.attrs
.attr_name_value_lookup("export_name")
.or_else(|| {
if self.is_no_mangle() {
Some(self.sig.ident.to_string())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really work for impl items? Like, does impl Foo { extern fn bar(&self) { ... } } really produce a bar symbol?

} else {
None
}
})
}
}

pub trait SynItemHelpers {
/// Searches for attributes like `#[test]`.
/// Example:
Expand Down Expand Up @@ -138,6 +152,7 @@ impl_syn_item_helper!(syn::ItemUse);
impl_syn_item_helper!(syn::ItemStatic);
impl_syn_item_helper!(syn::ItemConst);
impl_syn_item_helper!(syn::ItemFn);
impl_syn_item_helper!(syn::ImplItemMethod);
impl_syn_item_helper!(syn::ItemMod);
impl_syn_item_helper!(syn::ItemForeignMod);
impl_syn_item_helper!(syn::ItemType);
Expand Down
22 changes: 22 additions & 0 deletions tests/expectations/both/swift_name.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct Foo Foo;

typedef struct FooRef {
Foo *ptr;
} FooRef;

FooRef FooRef_create(void) CF_SWIFT_NAME(FooRef.create());

int32_t FooRef_doThing(FooRef self) /*a comment!*/ CF_SWIFT_NAME(FooRef.doThing(self:));

int32_t FooRef_getBar(FooRef self) CF_SWIFT_NAME(FooRef.getBar(self:));

void FooRef_setBar(FooRef self, int32_t bar) CF_SWIFT_NAME(FooRef.setBar(self:bar:));

void do_the_thing(void) CF_SWIFT_NAME(do_the_thing());
Loading