This page provides a high-level view of how generated source is supported and how it can be used in the build system.
All source generators provide similar build-system functionality. The three build-system supported source-generation use cases are generating C bindings using bindgen, AIDL interfaces, and protobuf interfaces.
Crates from generated source
Every Rust module that generates source code can be used as a crate, exactly as
if it were defined as a rust_library
. (This means it can be defined as a
dependency in the rustlibs
, rlibs
, and dylibs
properties.) The best usage
pattern for platform code is to employ generated source as a crate. Although the
include!
macro is supported for generated source, its primary purpose is to
support third-party code that resides in external/
.
There are cases where platform code might still use generated source through the
include!()
macro, such as when you use a genrule
module to generate source
in a unique fashion.
Use include!() to include generated source
Using generated source as a crate is covered by the examples in each specific
(respective) module page. This section shows how to reference generated source
through the include!()
macro. Note that this process is similar for all source
generators.
Prerequisite
This example is based on the assumption that you have defined a rust_bindgen
module (libbuzz_bindgen
) and can proceed to the Steps for including generated source
for using theinclude!()
macro. If you haven't, please go to Defining a rust bindgen module,
create libbuzz_bindgen
, then return here.
Note that the build-file portions of this are applicable for all source generators.
Steps for including generated source
Create external/rust/hello_bindgen/Android.bp
with the following contents:
rust_binary {
name: "hello_bzip_bindgen_include",
srcs: [
// The primary rust source file must come first in this list.
"src/lib.rs",
// The module providing the bindgen bindings is
// included in srcs prepended by ":".
":libbuzz_bindgen",
],
// Dependencies need to be redeclared when generated source is used via srcs.
shared_libs: [
"libbuzz",
],
}
Create external/rust/hello_bindgen/src/bindings.rs
with the following contents:
#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
#![allow(missing_docs)]
// Note that "bzip_bindings.rs" here must match the source_stem property from
// the rust_bindgen module.
include!(concat!(env!("OUT_DIR"), "/bzip_bindings.rs"));
Create external/rust/hello_bindgen/src/lib.rs
with the following contents:
mod bindings;
fn main() {
let mut x = bindings::foo { x: 2 };
unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}
Why crates for generated source
Unlike C/C++ compilers, rustc
only accepts a single source file
representing an entry point to a binary or library. It expects that the source
tree is structured such that all required source files can be automatically
discovered. This means that generated source must either be placed in the source
tree, or provided through an include directive in source:
include!("/path/to/hello.rs");
The Rust community depends on build.rs
scripts, and assumptions about
the Cargo build environment, to work with this difference.
When it builds, the cargo
command sets an OUT_DIR
environment variable
into which build.rs
scripts are expected to place generated source code. Use the
following command to include source code:
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
This presents a challenge for Soong as outputs for each module are placed in
their own out/
directory1. There isn't a single OUT_DIR
where
dependencies output their generated source.
For platform code, AOSP prefers packaging generated source into a crate that can be imported, for several reasons:
- Prevent generated source file names from colliding.
- Reduce boilerplate code checked-in throughout the tree that requires maintenance. Any boilerplate that's required to make the generated source compile into a crate can be centrally maintained.
- Avoid implicit2 interactions between generated code and the surrounding crate.
- Reduce pressure on memory and disk by dynamically linking commonly used generated sources.
As a result, all of Android’s Rust source generation module types produce code
that can be compiled and used as a crate.
Soong still supports third-party crates without modification if all
the generated source dependencies for a module get copied into a single per-module
directory, similar to Cargo. In such cases, Soong sets the OUT_DIR
environment variable
to that directory when compiling the module, so the generated source can be found.
However, for the reasons already described, it's best practice to only use
this mechanism in platform code when it's absolutely necessary.
-
This doesn't present any problems for C/C++ and similar languages, as the path to the generated source is provided directly to the compiler. ↩
-
Since
include!
works by textual inclusion, it might reference values from the enclosing namespace, modify the namespace, or use constructs like#![foo]
. These implicit interactions can be difficult to maintain. Always prefer macros when interaction with the rest of the crate is truly required. ↩