prebindgen/
lib.rs

1//! # prebindgen
2//!
3//! A tool for separating the implementation of FFI interfaces from language-specific binding generation,
4//! allowing each to reside in different crates.
5//!
6//! See also: [`prebindgen-proc-macro`](https://docs.rs/prebindgen-proc-macro) for the procedural macros.
7//!
8//! ## Problem
9//!
10//! When creating Rust libraries that need to expose FFI interfaces to multiple languages, 
11//! it may be preferable to create separate `cdylib` or `staticlib` crates for each language-specific binding. 
12//! This allows you to tailor each crate to the requirements and quirks of its binding generator and to specifisc of the
13//! destination language.
14//! However, `#[no_mangle] extern "C"` functions can only be defined in a `cdylib` or `staticlib` crate, and cannot be
15//! exported from a `lib` crate. As a result, these functions must be duplicated in each language-specific
16//! binding crate. This duplication is inconvenient for large projects with many FFI functions and types.
17//!
18//! ## Solution
19//!
20//! `prebindgen` solves this by generating `#[no_mangle] extern "C"` Rust proxy source code from a common
21//! Rust library crate.
22//! Language-specific binding crates can then compile this generated code and pass it to their respective
23//! binding generators (such as cbindgen, csbindgen, etc.).
24//!
25//! ## Usage example
26//!
27//! See also example projects on <https://github.com/milyin/prebindgen/tree/main/examples>
28//!
29//! See also the prebindgen-proc-macro documentation for details on how to use the `#[prebindgen]` macro:
30//! <https://docs.rs/prebindgen-proc-macro/latest/prebindgen_proc_macro/>
31//!
32//! ### 1. In the Common FFI Library Crate (e.g., `example_ffi`)
33//!
34//! Mark structures and functions that are part of the FFI interface with the `prebindgen` macro:
35//!
36//! ```rust,ignore
37//! // example-ffi/src/lib.rs
38//! use prebindgen_proc_macro::{prebindgen, prebindgen_out_dir};
39//!
40//! // Declare a public constant with the path to prebindgen data:
41//! pub const PREBINDGEN_OUT_DIR: &str = prebindgen_out_dir!();
42//!
43//! // Group structures and functions for selective handling
44//! #[prebindgen]
45//! #[repr(C)]
46//! pub struct MyStruct {
47//!     pub field: i32,
48//! }
49//!
50//! #[prebindgen]
51//! pub fn my_function(arg: i32) -> i32 {
52//!     arg * 2
53//! }
54//! ```
55//!
56//! Call [`init_prebindgen_out_dir`] in the crate's `build.rs`:
57//!
58//! ```rust,no_run
59//! // example-ffi/build.rs
60//! prebindgen::init_prebindgen_out_dir();
61//! ```
62//!
63//! ### 2. In Language-Specific FFI Binding Crate (named e.g. `example-cbindgen`)
64//!
65//! Add the common FFI library to build dependencies
66//!
67//! ```toml
68//! # example-cbindgen/Cargo.toml
69//! [build-dependencies]
70//! example_ffi = { path = "../example_ffi" }
71//! prebindgen = "0.2"
72//! cbindgen = "0.24"
73//! ```
74//! ```rust,ignore
75//! // example-cbindgen/build.rs
76//! use prebindgen::{Source, batching::ffi_converter, filter_map::feature_filter, collect::Destination};
77//! use itertools::Itertools;
78//!
79//! fn main() {
80//!     // Create a source from the common FFI crate's prebindgen data
81//!     let source = Source::new(my_common_ffi::PREBINDGEN_OUT_DIR);
82//!
83//!     // Process items with filtering and conversion
84//!     let destination = source
85//!         .items_all()
86//!         .filter_map(feature_filter::Builder::new()
87//!             .disable_feature("experimental")
88//!             .enable_feature("std")
89//!             .build()
90//!             .into_closure())
91//!         .batching(ffi_converter::Builder::new(source.crate_name())
92//!             .edition("2024")
93//!             .strip_transparent_wrapper("std::mem::MaybeUninit")
94//!             .build()
95//!             .into_closure())
96//!         .collect::<Destination>();
97//!
98//!     // Write generated FFI code to file
99//!     let bindings_file = destination.write("ffi_bindings.rs");
100//!
101//!     // Pass the generated file to cbindgen for C header generation
102//!     generate_c_headers(&bindings_file);
103//! }
104//! ```
105//!
106//! Include the generated Rust files in your project to build the static or dynamic library itself:
107//!
108//! ```rust,ignore
109//! // lib.rs
110//! include!(concat!(env!("OUT_DIR"), "/ffi_bindings.rs"));
111//! ```
112//!
113
114/// File name for storing the crate name
115const CRATE_NAME_FILE: &str = "crate_name.txt";
116
117/// Default group name for items without explicit group name
118pub const DEFAULT_GROUP_NAME: &str = "default";
119
120pub(crate) mod api;
121pub(crate) mod codegen;
122pub(crate) mod utils;
123
124pub use crate::api::buildrs::init_prebindgen_out_dir;
125pub use crate::api::record::SourceLocation;
126pub use crate::api::source::Source;
127
128/// Filters for sequences of (syn::Item, SourceLocation) called by `itertools::batching`
129pub mod batching {
130    pub mod ffi_converter {
131        pub use crate::api::batching::ffi_converter::Builder;
132    }
133    pub use crate::api::batching::ffi_converter::FfiConverter;
134}
135
136/// Filters for sequences of (syn::Item, SourceLocation) called by `filter_map`
137pub mod filter_map {
138    pub use crate::api::filter_map::feature_filter::FeatureFilter;
139    pub use crate::api::filter_map::struct_align::struct_align;
140    pub mod feature_filter {
141        pub use crate::api::filter_map::feature_filter::Builder;
142    }
143}
144
145/// Filters for sequences of (syn::Item, SourceLocation) called by `map`
146pub mod map {
147    pub use crate::api::map::strip_derive::StripDerives;
148    pub mod strip_derive {
149        pub use crate::api::map::strip_derive::Builder;
150    }
151    pub use crate::api::map::strip_macro::StripMacros;
152    pub mod strip_macro {
153        pub use crate::api::map::strip_macro::Builder;
154    }
155    pub use crate::api::map::replace_types::ReplaceTypes;
156    pub mod replace_types {
157        pub use crate::api::map::replace_types::Builder;
158    }
159}
160
161/// Collectors for sequences of (syn::Item, SourceLocation) produced by `collect`
162pub mod collect {
163    pub use crate::api::collect::destination::Destination;
164}
165
166#[doc(hidden)]
167pub use crate::api::buildrs::get_prebindgen_out_dir;
168#[doc(hidden)]
169pub use crate::api::record::Record;
170#[doc(hidden)]
171pub use crate::api::record::RecordKind;
172#[doc(hidden)]
173pub use crate::api::source::DOCTEST_SIMULATE_PREBINDGEN_OUT_DIR;
174
175/// Macro for setting up doctest environment with source_ffi module
176#[doc(hidden)]
177#[macro_export]
178macro_rules! doctest_setup {
179    () => {
180        mod source_ffi {
181            pub const PREBINDGEN_OUT_DIR: &str = prebindgen::DOCTEST_SIMULATE_PREBINDGEN_OUT_DIR;
182        }
183    };
184}