Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# unreleased

* Add `export.include_all` option to automatically export the public non-opaque
types of the binding crate.
* Emit `&str`, `&CStr`, and arrays thereof as C string literals, and byte-string
(`b"..."`) constants as `uint8_t[]` arrays, instead of dropping them.

Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ exclude = [
"tests/rust/expand_no_default_features",
"tests/rust/expand",
"tests/rust/external_workspace_child",
"tests/rust/include_all_deps",
"tests/rust/include_all_deps/dep",
"tests/rust/literal_target",
"tests/rust/mod_2015",
"tests/rust/mod_2018",
Expand Down
15 changes: 15 additions & 0 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,21 @@ usize_is_size_t = true
# default: []
include = ["MyOrphanStruct", "MyGreatTypeRename"]

# Whether to include all public, non-opaque types parsed by cbindgen (`#[repr(C)]`
# structs/unions, `#[repr(u*)]`/`#[repr(C)]` enums, `#[repr(transparent)]` types,
# and typedefs), even if they are not used by any exported functions. Visibility
# of parent modules is not considered: a type declared `pub` in a private module
# is included. Opaque items are included only when named by `include` or required
# by another exported item.
#
# Only the binding crate and the crates listed in `parse.extra_bindings` are
# considered, the same scope in which `pub` constants and `#[no_mangle]`
# functions are picked up. Types of other parsed crates are still emitted when
# an exported item depends on them.
#
# default: false
include_all = false

# A list of items to not include in the generated bindings
# default: []
exclude = ["Bad"]
Expand Down
7 changes: 7 additions & 0 deletions src/bindgen/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ impl Builder {
self
}

#[allow(unused)]
pub fn with_include_all(mut self, include_all: bool) -> Builder {
self.config.export.include_all = include_all;
self
}

#[allow(unused)]
pub fn exclude_item<S: AsRef<str>>(mut self, item_name: S) -> Builder {
self.config
Expand Down Expand Up @@ -411,6 +417,7 @@ impl Builder {
result.opaque_items,
result.typedefs,
result.functions,
result.public_types,
result.source_files,
result.package_version,
)
Expand Down
6 changes: 6 additions & 0 deletions src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,12 @@ pub struct ExportConfig {
/// A list of additional items not used by exported functions to include in
/// the generated bindings
pub include: Vec<String>,
/// Whether to include all public, non-opaque types in the generated bindings,
/// even if they are not used by any exported functions. Parent module
/// visibility is not considered. Opaque types are included only when named
/// by `include` or required by another exported item. Restricted to the
/// binding crate and `parse.extra_bindings`, like functions and constants.
pub include_all: bool,
/// A list of items to not include in the generated bindings
pub exclude: Vec<String>,
/// Table of name conversions to apply to item names
Expand Down
38 changes: 27 additions & 11 deletions src/bindgen/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ pub struct Library {
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
/// Paths of the `pub`, non-opaque types declared by the crates that are
/// allowed to contribute top-level items. Used as dependency roots when
/// `export.include_all` is set.
public_types: Vec<Path>,
source_files: Vec<PathBuf>,
package_version: String,
}
Expand All @@ -42,6 +46,7 @@ impl Library {
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
public_types: Vec<Path>,
source_files: Vec<PathBuf>,
package_version: String,
) -> Library {
Expand All @@ -55,6 +60,7 @@ impl Library {
opaque_items,
typedefs,
functions,
public_types,
source_files,
package_version,
}
Expand Down Expand Up @@ -90,19 +96,14 @@ impl Library {
self.constants.for_all_items(|constant| {
constant.add_dependencies(&self, &mut dependencies);
});
for name in &self.config.export.include {
let path = Path::new(name.clone());
if let Some(items) = self.get_items(&path) {
if dependencies.items.insert(path) {
for item in &items {
item.deref().add_dependencies(&self, &mut dependencies);
}
for item in items {
dependencies.order.push(item);
}
}
if self.config.export.include_all {
for path in &self.public_types {
self.add_root(&mut dependencies, path.clone());
}
}
for name in &self.config.export.include {
self.add_root(&mut dependencies, Path::new(name.clone()));
}

dependencies.sort();

Expand Down Expand Up @@ -149,6 +150,21 @@ impl Library {
))
}

/// Adds `path`, and everything it depends on, to `dependencies`. Paths that
/// don't resolve to a generated item are silently ignored.
fn add_root(&self, dependencies: &mut Dependencies, path: Path) {
let Some(items) = self.get_items(&path) else {
return;
};
if !dependencies.items.insert(path) {
return;
}
for item in &items {
item.deref().add_dependencies(self, dependencies);
}
dependencies.order.extend(items);
}

pub fn get_items(&self, p: &Path) -> Option<Vec<ItemContainer>> {
macro_rules! find {
($field:ident, $kind:ident) => {
Expand Down
49 changes: 40 additions & 9 deletions src/bindgen/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,10 @@ pub struct Parse {
pub functions: Vec<Function>,
pub source_files: Vec<FilePathBuf>,
pub package_version: String,
/// Paths of the `pub`, non-opaque types declared by the crates that are
/// allowed to contribute top-level items. Used as dependency roots when
/// `export.include_all` is set.
pub public_types: Vec<Path>,
}

impl Parse {
Expand All @@ -435,6 +439,7 @@ impl Parse {
functions: Vec::new(),
source_files: Vec::new(),
package_version: String::new(),
public_types: Vec::new(),
}
}

Expand Down Expand Up @@ -484,6 +489,13 @@ impl Parse {
self.functions.extend_from_slice(&other.functions);
self.source_files.extend_from_slice(&other.source_files);
self.package_version.clone_from(&other.package_version);
self.public_types.extend_from_slice(&other.public_types);
}

fn record_public_type(&mut self, record: bool, vis: &syn::Visibility, path: &Path) {
if record && matches!(vis, syn::Visibility::Public(_)) {
Comment on lines +495 to +496

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I feed awkward that record_public_type and its callers have a parameter like record: bool, but didn't find good way not to do yet.

self.public_types.push(path.clone());
}
}

fn load_syn_crate_mod<'a>(
Expand All @@ -497,6 +509,13 @@ impl Parse {
let mut impls_with_assoc_consts = Vec::new();
let mut nested_modules = Vec::new();

// Types are parsed from every crate, since exported items may reference
// them, but only the crates that contribute top-level items may seed the
// root set of `export.include_all`.
let record_public_types = config
.parse
.should_generate_top_level_item(crate_name, binding_crate_name);

for item in items {
if item.should_skip_parsing() {
continue;
Expand All @@ -521,16 +540,16 @@ impl Parse {
self.load_syn_static(config, binding_crate_name, crate_name, mod_cfg, item);
}
syn::Item::Struct(ref item) => {
self.load_syn_struct(config, crate_name, mod_cfg, item);
self.load_syn_struct(config, record_public_types, crate_name, mod_cfg, item);
}
syn::Item::Union(ref item) => {
self.load_syn_union(config, crate_name, mod_cfg, item);
self.load_syn_union(config, record_public_types, crate_name, mod_cfg, item);
}
syn::Item::Enum(ref item) => {
self.load_syn_enum(config, crate_name, mod_cfg, item);
self.load_syn_enum(config, record_public_types, crate_name, mod_cfg, item);
}
syn::Item::Type(ref item) => {
self.load_syn_ty(crate_name, mod_cfg, item);
self.load_syn_ty(record_public_types, crate_name, mod_cfg, item);
}
syn::Item::Impl(ref item_impl) => {
let has_assoc_const = item_impl
Expand Down Expand Up @@ -562,7 +581,7 @@ impl Parse {
}
}
syn::Item::Macro(ref item) => {
self.load_builtin_macro(config, crate_name, mod_cfg, item);
self.load_builtin_macro(config, record_public_types, crate_name, mod_cfg, item);
}
syn::Item::Mod(ref item) => {
nested_modules.push(item);
Expand Down Expand Up @@ -909,13 +928,15 @@ impl Parse {
fn load_syn_struct(
&mut self,
config: &Config,
record_public_types: bool,
crate_name: &str,
mod_cfg: Option<&Cfg>,
item: &syn::ItemStruct,
) {
match Struct::load(&config.layout, item, mod_cfg) {
Ok(st) => {
info!("Take {}::{}.", crate_name, item.ident);
self.record_public_type(record_public_types, &item.vis, &st.path);
self.structs.try_insert(st);
}
Err(msg) => {
Expand All @@ -932,14 +953,15 @@ impl Parse {
fn load_syn_union(
&mut self,
config: &Config,
record_public_types: bool,
crate_name: &str,
mod_cfg: Option<&Cfg>,
item: &syn::ItemUnion,
) {
match Union::load(&config.layout, item, mod_cfg) {
Ok(st) => {
info!("Take {}::{}.", crate_name, item.ident);

self.record_public_type(record_public_types, &item.vis, &st.path);
self.unions.try_insert(st);
}
Err(msg) => {
Expand All @@ -956,13 +978,15 @@ impl Parse {
fn load_syn_enum(
&mut self,
config: &Config,
record_public_types: bool,
crate_name: &str,
mod_cfg: Option<&Cfg>,
item: &syn::ItemEnum,
) {
match Enum::load(item, mod_cfg, config) {
Ok(en) => {
info!("Take {}::{}.", crate_name, item.ident);
self.record_public_type(record_public_types, &item.vis, &en.path);
self.enums.try_insert(en);
}
Err(msg) => {
Expand All @@ -976,11 +1000,17 @@ impl Parse {
}

/// Loads a `type` declaration
fn load_syn_ty(&mut self, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemType) {
fn load_syn_ty(
&mut self,
record_public_types: bool,
crate_name: &str,
mod_cfg: Option<&Cfg>,
item: &syn::ItemType,
) {
match Typedef::load(item, mod_cfg) {
Ok(st) => {
info!("Take {}::{}.", crate_name, item.ident);

self.record_public_type(record_public_types, &item.vis, &st.path);
self.typedefs.try_insert(st);
}
Err(msg) => {
Expand All @@ -996,6 +1026,7 @@ impl Parse {
fn load_builtin_macro(
&mut self,
config: &Config,
record_public_types: bool,
crate_name: &str,
mod_cfg: Option<&Cfg>,
item: &syn::ItemMacro,
Expand Down Expand Up @@ -1028,7 +1059,7 @@ impl Parse {

let (struct_, impl_) = bitflags.expand(out_of_line_transparent);
if let Some(struct_) = struct_ {
self.load_syn_struct(config, crate_name, mod_cfg, &struct_);
self.load_syn_struct(config, record_public_types, crate_name, mod_cfg, &struct_);
}
if let syn::Type::Path(ref path) = *impl_.self_ty {
if let Some(type_name) = path.path.get_ident() {
Expand Down
1 change: 1 addition & 0 deletions template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ usize_is_size_t = true

[export]
include = []
include_all = false
exclude = []
# prefix = "CAPI_"
item_types = []
Expand Down
2 changes: 2 additions & 0 deletions tests/expectations-symbols/include_all.c.sym
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
};
3 changes: 3 additions & 0 deletions tests/expectations-symbols/include_all_deps.c.sym
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
get_x;
};
44 changes: 44 additions & 0 deletions tests/expectations/include_all.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

enum UnusedEnum
#if __STDC_VERSION__ >= 202311L
: uint8_t
#endif // __STDC_VERSION__ >= 202311L
{
A,
B,
};
#if __STDC_VERSION__ >= 202311L
typedef enum UnusedEnum UnusedEnum;
#else
typedef uint8_t UnusedEnum;
#endif // __STDC_VERSION__ >= 202311L

typedef struct ExplicitOpaque ExplicitOpaque;

typedef struct OpaqueDependency OpaqueDependency;

typedef struct {
int32_t x;
float y;
} UnusedStruct;

typedef uint32_t UnusedTransparent;

typedef union {
int32_t x;
float y;
} UnusedUnion;

typedef int32_t UnusedAlias;

typedef struct {
OpaqueDependency *opaque;
} UsesOpaqueDependency;

typedef struct {
int32_t x;
} PublicInPrivateModule;
Loading
Loading