Expand description
The build frontend.
builder::Config
holds the builder configuration. It is created with the
default parameters by builder::Config::default
.
The builder is then configured by the methods on builder::Config
. Both
the source files (*.c
, *.cc
, *.s
, …) and header files
(*.h
, *.hh
, …) can be configured together as the input files. At
least one input file MUST be configured, either source or header.
The backends can also be configured via builder::Config
. In order to
make the kusabira
API robust against the changes on the backend
configuration design, they are configured by the hooks, namely the
user-supplied function or closure that receives the backend-dependent
configuration, modifies it and returns it.
Finally, the builder is executed by builder::Config::build
, which
builds the library and
Rust FFI binding files
altogether. builder::Config::build
checks the extention of each input
file; the source files are passed together to cc::Build::try_compile
to
build a single library, while the header files are passed in the one-by-one
manner to bindgen::Builder::generate
and
bindgen::Bindings::write_to_file
. Both of these behaviors reflect the
usage design of the backends.
The glob expansion by glob
is supported on the input files. The glob
expansion happens during the execution of builder::Config::build
.
All of the input files discovered by builder::Config::build
are
reported to Cargo
by
cargo:rerun-if-changed
,
so that an update on any input files trigger the rebuild. This includes
the recursively included header files found by
bindgen::Builder::generate
.
RECOMMENDED Input File Configuration
Source files
Configure all of them together, possibly by the glob. They are all
compiled into a single library, which is then reported to
Cargo
by builder::Config::build
for linking.
Header files
Create a single header file that #include
s all of the header files
exported to Rust. Configure only this header file to builder::Config
.
On the Rust side, include!
the generated binding file
into a Rust source file. Each binding file has the same filename as the
configured header file except that the extention is replaced by the one
configured by builder::Config::binding_ext
, or
builder::RUST_FFI_BINDING_EXT
by default.
This configuration is recommended because bindgen::Builder::generate
requires a header file completely pre-processable and compilable on its
own, while most header files depend on some other header files
#include
d before them.
Mind that the identifier scope of Rust is different from C/C++/assembly.
In Rust, you include!
a binding file somewhere in a
module, and all of the identifiers in it are visible anywhere in the
module.
Also note that the generated binding file SHOULD NOT be compiled directly.
rustc
assumes the certain source file
hierarchy to define the modules, which requires some tricks to follow. In
addition, Cargo
may exhibit an error
or unexpected behavior if the build script emits any files into the source
directory.
A good practice in a large-scaled project is to include!
the binding file into a dedicated module, and use
the required items only
to avoid flooding a module by many unused identifiers. As of version
0.68.1, bindgen
adds pub
to every bound identifier.
Alternatively, you can also create multiple header files and configure all of them, as long as each header file pre-processes and compiles on its own. This is a good option if you have multiple features to import and each Rust module does not require all imported features.
Internal Design Notes
Path Storage
The output directory is stored as std::path::PathBuf
because it MUST
point to a valid directory path upon building.
In contrary, the source file glob patterns are represented by
std::string::String
. A glob pattern do not necessarily have to be a
valid path, so std::path::PathBuf
is too restrictive. glob
deals
with a pattern in the same way.
Hook Addition
The following methods on builder::Config
add a new hook to the existing
one in the configuration:
builder::Config::add_cc_build_hook
builder::Config::add_bindgen_builder_hook
builder::Config::add_glob_matchoptions_hook
Internally, these methods create a new hook closure in which the input
parameter is first passed to the existing hook, and its result is then
passed to the new hook. Below is the pseudocode of the internal hook
closure created by builder::Config::add_cc_build_hook
:
use cc::Build;
use std::ops::FnOnce;
let existing_hook: Box<dyn FnOnce(&mut Build) -> &mut Build>
= Box::new(|build: &mut Build|
{
// Work on build.
build
});
let added_hook: Box<dyn FnOnce(&mut Build) -> &mut Build>
= Box::new(|build: &mut Build|
{
// Another work on build.
build
});
let new_hook: Box<dyn FnOnce(&mut Build) -> &mut Build>
= Box::new(
move |build: &mut Build|
{
added_hook(existing_hook(build))
});
The other methods create the internal hook closure in the same way except for the parameter and return type, and the hook function trait.
Structs
- The build results returned by
Config::build
. - The configuration parameters, as well as the entry to the builder engine.
- The pair of an input header file and the generated Rust FFI binding file, held in
BuildResults
.
Statics
- The environment variable name
Cargo
configures the output directory. - The default path extensions for the header files passed to
bindgen::Builder::generate
. - The default extension of the Rust FFI binding file.
- The default path extensions for the source files passed to
cc::Build
.