diff --git a/CHANGES b/CHANGES index 6fd28678..bcdaadec 100644 --- a/CHANGES +++ b/CHANGES @@ -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. diff --git a/Cargo.toml b/Cargo.toml index e279f2b0..3bcba073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", diff --git a/docs.md b/docs.md index aa071ac6..68c61ada 100644 --- a/docs.md +++ b/docs.md @@ -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"] diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index 8521b706..e7912b15 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -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>(mut self, item_name: S) -> Builder { self.config @@ -411,6 +417,7 @@ impl Builder { result.opaque_items, result.typedefs, result.functions, + result.public_types, result.source_files, result.package_version, ) diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index acffa3b2..5cda6db2 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -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, + /// 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, /// Table of name conversions to apply to item names diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index 9bccceb5..1a1cba30 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -26,6 +26,10 @@ pub struct Library { opaque_items: ItemMap, typedefs: ItemMap, functions: Vec, + /// 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, source_files: Vec, package_version: String, } @@ -42,6 +46,7 @@ impl Library { opaque_items: ItemMap, typedefs: ItemMap, functions: Vec, + public_types: Vec, source_files: Vec, package_version: String, ) -> Library { @@ -55,6 +60,7 @@ impl Library { opaque_items, typedefs, functions, + public_types, source_files, package_version, } @@ -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(); @@ -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> { macro_rules! find { ($field:ident, $kind:ident) => { diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index 7f004d1a..26ea1c04 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -420,6 +420,10 @@ pub struct Parse { pub functions: Vec, pub source_files: Vec, 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, } impl Parse { @@ -435,6 +439,7 @@ impl Parse { functions: Vec::new(), source_files: Vec::new(), package_version: String::new(), + public_types: Vec::new(), } } @@ -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(_)) { + self.public_types.push(path.clone()); + } } fn load_syn_crate_mod<'a>( @@ -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; @@ -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 @@ -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); @@ -909,6 +928,7 @@ impl Parse { fn load_syn_struct( &mut self, config: &Config, + record_public_types: bool, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemStruct, @@ -916,6 +936,7 @@ impl Parse { 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) => { @@ -932,6 +953,7 @@ impl Parse { fn load_syn_union( &mut self, config: &Config, + record_public_types: bool, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemUnion, @@ -939,7 +961,7 @@ impl Parse { 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) => { @@ -956,6 +978,7 @@ impl Parse { fn load_syn_enum( &mut self, config: &Config, + record_public_types: bool, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemEnum, @@ -963,6 +986,7 @@ impl Parse { 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) => { @@ -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) => { @@ -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, @@ -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() { diff --git a/template.toml b/template.toml index 1c416aa1..88476077 100644 --- a/template.toml +++ b/template.toml @@ -59,6 +59,7 @@ usize_is_size_t = true [export] include = [] +include_all = false exclude = [] # prefix = "CAPI_" item_types = [] diff --git a/tests/expectations-symbols/include_all.c.sym b/tests/expectations-symbols/include_all.c.sym new file mode 100644 index 00000000..33e9f352 --- /dev/null +++ b/tests/expectations-symbols/include_all.c.sym @@ -0,0 +1,2 @@ +{ +}; \ No newline at end of file diff --git a/tests/expectations-symbols/include_all_deps.c.sym b/tests/expectations-symbols/include_all_deps.c.sym new file mode 100644 index 00000000..3ac65782 --- /dev/null +++ b/tests/expectations-symbols/include_all_deps.c.sym @@ -0,0 +1,3 @@ +{ +get_x; +}; \ No newline at end of file diff --git a/tests/expectations/include_all.c b/tests/expectations/include_all.c new file mode 100644 index 00000000..000bc4e3 --- /dev/null +++ b/tests/expectations/include_all.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +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; diff --git a/tests/expectations/include_all.compat.c b/tests/expectations/include_all.compat.c new file mode 100644 index 00000000..dcc2eec9 --- /dev/null +++ b/tests/expectations/include_all.compat.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +enum UnusedEnum +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + A, + B, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum UnusedEnum UnusedEnum; +#else +typedef uint8_t UnusedEnum; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +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; diff --git a/tests/expectations/include_all.cpp b/tests/expectations/include_all.cpp new file mode 100644 index 00000000..b67d3cda --- /dev/null +++ b/tests/expectations/include_all.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +enum class UnusedEnum : uint8_t { + A, + B, +}; + +struct ExplicitOpaque; + +struct OpaqueDependency; + +struct UnusedStruct { + int32_t x; + float y; +}; + +using UnusedTransparent = uint32_t; + +union UnusedUnion { + int32_t x; + float y; +}; + +using UnusedAlias = int32_t; + +struct UsesOpaqueDependency { + OpaqueDependency *opaque; +}; + +struct PublicInPrivateModule { + int32_t x; +}; diff --git a/tests/expectations/include_all.pyx b/tests/expectations/include_all.pyx new file mode 100644 index 00000000..0669f343 --- /dev/null +++ b/tests/expectations/include_all.pyx @@ -0,0 +1,36 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef enum: + A, + B, + ctypedef uint8_t UnusedEnum; + + ctypedef struct ExplicitOpaque: + pass + + ctypedef struct OpaqueDependency: + pass + + ctypedef struct UnusedStruct: + int32_t x; + float y; + + ctypedef uint32_t UnusedTransparent; + + ctypedef union UnusedUnion: + int32_t x; + float y; + + ctypedef int32_t UnusedAlias; + + ctypedef struct UsesOpaqueDependency: + OpaqueDependency *opaque; + + ctypedef struct PublicInPrivateModule: + int32_t x; diff --git a/tests/expectations/include_all_both.c b/tests/expectations/include_all_both.c new file mode 100644 index 00000000..534bdf78 --- /dev/null +++ b/tests/expectations/include_all_both.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +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 UnusedStruct { + int32_t x; + float y; +} UnusedStruct; + +typedef uint32_t UnusedTransparent; + +typedef union UnusedUnion { + int32_t x; + float y; +} UnusedUnion; + +typedef int32_t UnusedAlias; + +typedef struct UsesOpaqueDependency { + struct OpaqueDependency *opaque; +} UsesOpaqueDependency; + +typedef struct PublicInPrivateModule { + int32_t x; +} PublicInPrivateModule; diff --git a/tests/expectations/include_all_both.compat.c b/tests/expectations/include_all_both.compat.c new file mode 100644 index 00000000..a83a1ee9 --- /dev/null +++ b/tests/expectations/include_all_both.compat.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +enum UnusedEnum +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + A, + B, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum UnusedEnum UnusedEnum; +#else +typedef uint8_t UnusedEnum; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +typedef struct ExplicitOpaque ExplicitOpaque; + +typedef struct OpaqueDependency OpaqueDependency; + +typedef struct UnusedStruct { + int32_t x; + float y; +} UnusedStruct; + +typedef uint32_t UnusedTransparent; + +typedef union UnusedUnion { + int32_t x; + float y; +} UnusedUnion; + +typedef int32_t UnusedAlias; + +typedef struct UsesOpaqueDependency { + struct OpaqueDependency *opaque; +} UsesOpaqueDependency; + +typedef struct PublicInPrivateModule { + int32_t x; +} PublicInPrivateModule; diff --git a/tests/expectations/include_all_deps.c b/tests/expectations/include_all_deps.c new file mode 100644 index 00000000..cff97788 --- /dev/null +++ b/tests/expectations/include_all_deps.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +/** + * Reachable from an exported function, so it is emitted even though its crate + * does not contribute top-level items. + */ +typedef struct { + uint32_t x; +} UsedDepStruct; + +/** + * Not used by any exported item, but declared by the binding crate. + */ +typedef struct { + uint32_t z; +} UnusedLocalStruct; + +uint32_t get_x(const UsedDepStruct *used); diff --git a/tests/expectations/include_all_deps.compat.c b/tests/expectations/include_all_deps.compat.c new file mode 100644 index 00000000..126ad26c --- /dev/null +++ b/tests/expectations/include_all_deps.compat.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +/** + * Reachable from an exported function, so it is emitted even though its crate + * does not contribute top-level items. + */ +typedef struct { + uint32_t x; +} UsedDepStruct; + +/** + * Not used by any exported item, but declared by the binding crate. + */ +typedef struct { + uint32_t z; +} UnusedLocalStruct; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +uint32_t get_x(const UsedDepStruct *used); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/include_all_deps.cpp b/tests/expectations/include_all_deps.cpp new file mode 100644 index 00000000..b276bdfd --- /dev/null +++ b/tests/expectations/include_all_deps.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include + +/// Reachable from an exported function, so it is emitted even though its crate +/// does not contribute top-level items. +struct UsedDepStruct { + uint32_t x; +}; + +/// Not used by any exported item, but declared by the binding crate. +struct UnusedLocalStruct { + uint32_t z; +}; + +extern "C" { + +uint32_t get_x(const UsedDepStruct *used); + +} // extern "C" diff --git a/tests/expectations/include_all_deps.pyx b/tests/expectations/include_all_deps.pyx new file mode 100644 index 00000000..7ccd77db --- /dev/null +++ b/tests/expectations/include_all_deps.pyx @@ -0,0 +1,18 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + # Reachable from an exported function, so it is emitted even though its crate + # does not contribute top-level items. + ctypedef struct UsedDepStruct: + uint32_t x; + + # Not used by any exported item, but declared by the binding crate. + ctypedef struct UnusedLocalStruct: + uint32_t z; + + uint32_t get_x(const UsedDepStruct *used); diff --git a/tests/expectations/include_all_deps_both.c b/tests/expectations/include_all_deps_both.c new file mode 100644 index 00000000..29d309f4 --- /dev/null +++ b/tests/expectations/include_all_deps_both.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +/** + * Reachable from an exported function, so it is emitted even though its crate + * does not contribute top-level items. + */ +typedef struct UsedDepStruct { + uint32_t x; +} UsedDepStruct; + +/** + * Not used by any exported item, but declared by the binding crate. + */ +typedef struct UnusedLocalStruct { + uint32_t z; +} UnusedLocalStruct; + +uint32_t get_x(const struct UsedDepStruct *used); diff --git a/tests/expectations/include_all_deps_both.compat.c b/tests/expectations/include_all_deps_both.compat.c new file mode 100644 index 00000000..37d1f1de --- /dev/null +++ b/tests/expectations/include_all_deps_both.compat.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +/** + * Reachable from an exported function, so it is emitted even though its crate + * does not contribute top-level items. + */ +typedef struct UsedDepStruct { + uint32_t x; +} UsedDepStruct; + +/** + * Not used by any exported item, but declared by the binding crate. + */ +typedef struct UnusedLocalStruct { + uint32_t z; +} UnusedLocalStruct; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +uint32_t get_x(const struct UsedDepStruct *used); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/include_all_deps_tag.c b/tests/expectations/include_all_deps_tag.c new file mode 100644 index 00000000..71947987 --- /dev/null +++ b/tests/expectations/include_all_deps_tag.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +/** + * Reachable from an exported function, so it is emitted even though its crate + * does not contribute top-level items. + */ +struct UsedDepStruct { + uint32_t x; +}; + +/** + * Not used by any exported item, but declared by the binding crate. + */ +struct UnusedLocalStruct { + uint32_t z; +}; + +uint32_t get_x(const struct UsedDepStruct *used); diff --git a/tests/expectations/include_all_deps_tag.compat.c b/tests/expectations/include_all_deps_tag.compat.c new file mode 100644 index 00000000..63f8eec1 --- /dev/null +++ b/tests/expectations/include_all_deps_tag.compat.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +/** + * Reachable from an exported function, so it is emitted even though its crate + * does not contribute top-level items. + */ +struct UsedDepStruct { + uint32_t x; +}; + +/** + * Not used by any exported item, but declared by the binding crate. + */ +struct UnusedLocalStruct { + uint32_t z; +}; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +uint32_t get_x(const struct UsedDepStruct *used); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/include_all_deps_tag.pyx b/tests/expectations/include_all_deps_tag.pyx new file mode 100644 index 00000000..0e7c3e84 --- /dev/null +++ b/tests/expectations/include_all_deps_tag.pyx @@ -0,0 +1,18 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + # Reachable from an exported function, so it is emitted even though its crate + # does not contribute top-level items. + cdef struct UsedDepStruct: + uint32_t x; + + # Not used by any exported item, but declared by the binding crate. + cdef struct UnusedLocalStruct: + uint32_t z; + + uint32_t get_x(const UsedDepStruct *used); diff --git a/tests/expectations/include_all_tag.c b/tests/expectations/include_all_tag.c new file mode 100644 index 00000000..3ff1e2e2 --- /dev/null +++ b/tests/expectations/include_all_tag.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +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 + +struct ExplicitOpaque; + +struct OpaqueDependency; + +struct UnusedStruct { + int32_t x; + float y; +}; + +typedef uint32_t UnusedTransparent; + +union UnusedUnion { + int32_t x; + float y; +}; + +typedef int32_t UnusedAlias; + +struct UsesOpaqueDependency { + struct OpaqueDependency *opaque; +}; + +struct PublicInPrivateModule { + int32_t x; +}; diff --git a/tests/expectations/include_all_tag.compat.c b/tests/expectations/include_all_tag.compat.c new file mode 100644 index 00000000..f1d19ed1 --- /dev/null +++ b/tests/expectations/include_all_tag.compat.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +enum UnusedEnum +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + A, + B, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum UnusedEnum UnusedEnum; +#else +typedef uint8_t UnusedEnum; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +struct ExplicitOpaque; + +struct OpaqueDependency; + +struct UnusedStruct { + int32_t x; + float y; +}; + +typedef uint32_t UnusedTransparent; + +union UnusedUnion { + int32_t x; + float y; +}; + +typedef int32_t UnusedAlias; + +struct UsesOpaqueDependency { + struct OpaqueDependency *opaque; +}; + +struct PublicInPrivateModule { + int32_t x; +}; diff --git a/tests/expectations/include_all_tag.pyx b/tests/expectations/include_all_tag.pyx new file mode 100644 index 00000000..81cfdb57 --- /dev/null +++ b/tests/expectations/include_all_tag.pyx @@ -0,0 +1,36 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef enum: + A, + B, + ctypedef uint8_t UnusedEnum; + + cdef struct ExplicitOpaque: + pass + + cdef struct OpaqueDependency: + pass + + cdef struct UnusedStruct: + int32_t x; + float y; + + ctypedef uint32_t UnusedTransparent; + + cdef union UnusedUnion: + int32_t x; + float y; + + ctypedef int32_t UnusedAlias; + + cdef struct UsesOpaqueDependency: + OpaqueDependency *opaque; + + cdef struct PublicInPrivateModule: + int32_t x; diff --git a/tests/rust/include_all.rs b/tests/rust/include_all.rs new file mode 100644 index 00000000..3bb95b93 --- /dev/null +++ b/tests/rust/include_all.rs @@ -0,0 +1,71 @@ +#[repr(C)] +pub struct UnusedStruct { + x: i32, + y: f32, +} + +#[repr(u8)] +pub enum UnusedEnum { + A, + B, +} + +#[repr(transparent)] +pub struct UnusedTransparent(u32); + +#[repr(C)] +pub union UnusedUnion { + x: i32, + y: f32, +} + +pub type UnusedAlias = i32; + +pub struct NotCbindgenable { + x: i32, +} + +pub struct ExplicitOpaque { + x: i32, +} + +struct OpaqueDependency { + x: i32, +} + +#[repr(C)] +pub struct UsesOpaqueDependency { + opaque: *mut OpaqueDependency, +} + +#[repr(C)] +pub struct ExcludedStruct { + x: i32, +} + +#[repr(C)] +struct PrivateStruct { + x: i32, +} + +#[repr(u8)] +enum PrivateEnum { + A, +} + +#[repr(transparent)] +struct PrivateTransparent(u32); + +#[repr(C)] +union PrivateUnion { + x: i32, +} + +type PrivateAlias = i32; + +mod private_module { + #[repr(C)] + pub struct PublicInPrivateModule { + pub x: i32, + } +} diff --git a/tests/rust/include_all.toml b/tests/rust/include_all.toml new file mode 100644 index 00000000..b4e10a8d --- /dev/null +++ b/tests/rust/include_all.toml @@ -0,0 +1,4 @@ +[export] +include_all = true +include = ["ExplicitOpaque"] +exclude = ["ExcludedStruct"] diff --git a/tests/rust/include_all_deps/Cargo.lock b/tests/rust/include_all_deps/Cargo.lock new file mode 100644 index 00000000..d4d7981a --- /dev/null +++ b/tests/rust/include_all_deps/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "include-all-deps" +version = "0.1.0" +dependencies = [ + "include-all-deps-dep", +] + +[[package]] +name = "include-all-deps-dep" +version = "0.1.0" diff --git a/tests/rust/include_all_deps/Cargo.toml b/tests/rust/include_all_deps/Cargo.toml new file mode 100644 index 00000000..ed71cc8f --- /dev/null +++ b/tests/rust/include_all_deps/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "include-all-deps" +version = "0.1.0" +authors = ["cbindgen"] +edition = "2018" + +[dependencies] +include-all-deps-dep = { path = "dep" } diff --git a/tests/rust/include_all_deps/cbindgen.toml b/tests/rust/include_all_deps/cbindgen.toml new file mode 100644 index 00000000..4959ed94 --- /dev/null +++ b/tests/rust/include_all_deps/cbindgen.toml @@ -0,0 +1,6 @@ +[parse] +parse_deps = true +include = ["include-all-deps-dep"] + +[export] +include_all = true diff --git a/tests/rust/include_all_deps/dep/Cargo.toml b/tests/rust/include_all_deps/dep/Cargo.toml new file mode 100644 index 00000000..eed5a330 --- /dev/null +++ b/tests/rust/include_all_deps/dep/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "include-all-deps-dep" +version = "0.1.0" +authors = ["cbindgen"] +edition = "2018" + +[dependencies] diff --git a/tests/rust/include_all_deps/dep/src/lib.rs b/tests/rust/include_all_deps/dep/src/lib.rs new file mode 100644 index 00000000..4214c995 --- /dev/null +++ b/tests/rust/include_all_deps/dep/src/lib.rs @@ -0,0 +1,19 @@ +/// Reachable from an exported function, so it is emitted even though its crate +/// does not contribute top-level items. +#[repr(C)] +pub struct UsedDepStruct { + pub x: u32, +} + +/// Not reachable, and its crate is not listed in `parse.extra_bindings`, so +/// `export.include_all` must not pick it up. +#[repr(C)] +pub struct UnusedDepStruct { + pub y: u32, +} + +/// Same gate as the types above: never emitted from a non-binding crate. +#[no_mangle] +pub extern "C" fn dep_only_fn() -> u32 { + 0 +} diff --git a/tests/rust/include_all_deps/src/lib.rs b/tests/rust/include_all_deps/src/lib.rs new file mode 100644 index 00000000..065139b1 --- /dev/null +++ b/tests/rust/include_all_deps/src/lib.rs @@ -0,0 +1,12 @@ +use include_all_deps_dep::UsedDepStruct; + +/// Not used by any exported item, but declared by the binding crate. +#[repr(C)] +pub struct UnusedLocalStruct { + pub z: u32, +} + +#[no_mangle] +pub unsafe extern "C" fn get_x(used: *const UsedDepStruct) -> u32 { + (*used).x +}