1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-18 13:38:12 +00:00
trezor-firmware/core/embed/rust/build.rs
matejcik 2aa427a6e3 feat(core/rust): make Rust buildable without Micropython
Features `micropython` and `protobuf` are defined. Protobuf implies micropython
because our protobuf impl is pretty much _for_ micropython.

The respective subdirs are included only if the matching feature is defined.

util.rs is moved to micropython because it mostly concerns micropython interop

ResultExt, useful only for ui_debug, is moved to ui::util.

A new module `trezorhal::time` is provided. It mirrors functionality of
`micropython::time` via stmlib functions.  The intended use is to always use
functions from `trezorhal::time`. The right micropython variants are used when
micropython is available, otherwise the pure stmlib versions are called.

ui::*::layout is conditional for micropython feature, because it only concerns
micropython layouts. If we want to reuse layouts defined there, we will need to
export them to not depend on Objs and Qstrs etc.
2022-06-16 13:08:07 +02:00

331 lines
12 KiB
Rust

#[cfg(feature = "test")]
use std::ffi::OsStr;
use std::{env, path::PathBuf, process::Command};
fn main() {
#[cfg(feature = "micropython")]
generate_qstr_bindings();
#[cfg(feature = "micropython")]
generate_micropython_bindings();
generate_trezorhal_bindings();
#[cfg(feature = "test")]
link_core_objects();
}
fn model() -> String {
match env::var("TREZOR_MODEL") {
Ok(model) => model,
Err(_) => String::from("T"),
}
}
/// Generates Rust module that exports QSTR constants used in firmware.
#[cfg(feature = "micropython")]
fn generate_qstr_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
// Tell cargo to invalidate the built crate whenever the header changes.
println!("cargo:rerun-if-changed=qstr.h");
bindgen::Builder::default()
.header("qstr.h")
// Build the Qstr enum as a newtype so we can define method on it.
.default_enum_style(bindgen::EnumVariation::NewType { is_bitfield: false })
// Pass in correct include paths.
.clang_args(&[
"-I",
if is_firmware() {
"../../build/firmware"
} else {
"../../build/unix"
},
])
// Customize the standard types.
.use_core()
.ctypes_prefix("cty")
.size_t_is_usize(true)
// Tell cargo to invalidate the built crate whenever any of the
// included header files change.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to generate Rust QSTR bindings")
.write_to_file(PathBuf::from(out_path).join("qstr.rs"))
.unwrap();
}
fn prepare_bindings() -> bindgen::Builder {
let mut bindings = bindgen::Builder::default();
// Common include paths and defines
bindings = bindings.clang_args([
"-I../../../crypto",
"-I../../../storage",
"-I../../vendor/micropython",
"-I../extmod/modtrezorui", // for display.h
format!("-DTREZOR_MODEL_{}", model()).as_str(),
]);
// Pass in correct include paths and defines.
if is_firmware() {
bindings = bindings.clang_args(&[
"-nostdinc",
"-I../firmware",
"-I../trezorhal",
"-I../../build/firmware",
"-I../../vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc",
"-I../../vendor/micropython/lib/stm32lib/CMSIS/STM32F4xx/Include",
"-I../../vendor/micropython/lib/cmsis/inc",
"-DSTM32F427xx",
"-DUSE_HAL_DRIVER",
"-DSTM32_HAL_H=<stm32f4xx.h>",
]);
// Append gcc-arm-none-eabi's include paths.
let cc_output = Command::new("arm-none-eabi-gcc")
.arg("-E")
.arg("-Wp,-v")
.arg("-")
.output()
.expect("arm-none-eabi-gcc failed to execute");
if !cc_output.status.success() {
panic!("arm-none-eabi-gcc failed");
}
let include_paths =
String::from_utf8(cc_output.stderr).expect("arm-none-eabi-gcc returned invalid output");
let include_args = include_paths
.lines()
.skip_while(|s| !s.contains("search starts here:"))
.take_while(|s| !s.contains("End of search list."))
.filter(|s| s.starts_with(' '))
.map(|s| format!("-I{}", s.trim()));
bindings = bindings.clang_args(include_args);
} else {
bindings = bindings.clang_args(&[
"-I../unix",
"-I../../build/unix",
"-I../../vendor/micropython/ports/unix",
]);
}
bindings
// Customize the standard types.
.use_core()
.ctypes_prefix("cty")
.size_t_is_usize(true)
// Disable the layout tests. They spew out a lot of code-style bindings, and are not too
// relevant for our use-case.
.layout_tests(false)
// Tell cargo to invalidate the built crate whenever any of the
// included header files change.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
}
#[cfg(feature = "micropython")]
fn generate_micropython_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
// Tell cargo to invalidate the built crate whenever the header changes.
println!("cargo:rerun-if-changed=micropython.h");
let bindings = prepare_bindings()
.header("micropython.h")
// obj
.new_type_alias("mp_obj_t")
.allowlist_type("mp_obj_type_t")
.allowlist_type("mp_obj_base_t")
.allowlist_function("mp_obj_new_int")
.allowlist_function("mp_obj_new_int_from_ll")
.allowlist_function("mp_obj_new_int_from_ull")
.allowlist_function("mp_obj_new_int_from_uint")
.allowlist_function("mp_obj_new_bytes")
.allowlist_function("mp_obj_new_str")
.allowlist_function("mp_obj_new_tuple")
.allowlist_function("mp_obj_get_int_maybe")
.allowlist_function("mp_obj_is_true")
.allowlist_function("mp_call_function_n_kw")
.allowlist_function("trezor_obj_get_ll_checked")
.allowlist_function("trezor_obj_get_ull_checked")
.allowlist_function("trezor_obj_str_from_rom_text")
// buffer
.allowlist_function("mp_get_buffer")
.allowlist_var("MP_BUFFER_READ")
.allowlist_var("MP_BUFFER_WRITE")
.allowlist_var("MP_BUFFER_RW")
.allowlist_var("mp_type_str")
// dict
.allowlist_type("mp_obj_dict_t")
.allowlist_function("mp_obj_new_dict")
.allowlist_function("mp_obj_dict_store")
.allowlist_var("mp_type_dict")
// fun
.allowlist_type("mp_obj_fun_builtin_fixed_t")
.allowlist_var("mp_type_fun_builtin_1")
.allowlist_var("mp_type_fun_builtin_2")
.allowlist_var("mp_type_fun_builtin_3")
.allowlist_type("mp_obj_fun_builtin_var_t")
.allowlist_var("mp_type_fun_builtin_var")
// gc
.allowlist_function("gc_alloc")
// iter
.allowlist_type("mp_obj_iter_buf_t")
.allowlist_function("mp_getiter")
.allowlist_function("mp_iternext")
// list
.allowlist_type("mp_obj_list_t")
.allowlist_function("mp_obj_new_list")
.allowlist_function("mp_obj_list_append")
.allowlist_function("mp_obj_list_set_len")
.allowlist_var("mp_type_list")
// map
.allowlist_type("mp_map_elem_t")
.allowlist_type("mp_map_lookup_kind_t")
.allowlist_function("mp_map_init")
.allowlist_function("mp_map_init_fixed_table")
.allowlist_function("mp_map_lookup")
// exceptions
.allowlist_function("nlr_jump")
.allowlist_function("mp_obj_new_exception")
.allowlist_function("mp_obj_new_exception_args")
.allowlist_function("trezor_obj_call_protected")
.allowlist_var("mp_type_AttributeError")
.allowlist_var("mp_type_KeyError")
.allowlist_var("mp_type_MemoryError")
.allowlist_var("mp_type_OverflowError")
.allowlist_var("mp_type_ValueError")
.allowlist_var("mp_type_TypeError")
// time
.allowlist_function("mp_hal_ticks_ms")
.allowlist_function("mp_hal_delay_ms")
// typ
.allowlist_var("mp_type_type")
// module
.allowlist_type("mp_obj_module_t")
.allowlist_var("mp_type_module")
// `ffi::mp_map_t` type is not allowed to be `Clone` or `Copy` because we tie it
// to the data lifetimes with the `MapRef` type, see `src/micropython/map.rs`.
// TODO: We should disable `Clone` and `Copy` for all types and only allow-list
// the specific cases we require.
.no_copy("_mp_map_t");
// Write the bindings to a file in the OUR_DIR.
bindings
.generate()
.expect("Unable to generate bindings")
.write_to_file(PathBuf::from(out_path).join("micropython.rs"))
.unwrap();
}
fn generate_trezorhal_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
// Tell cargo to invalidate the built crate whenever the header changes.
println!("cargo:rerun-if-changed=trezorhal.h");
let bindings = prepare_bindings()
.header("trezorhal.h")
// secbool
.allowlist_type("secbool")
.must_use_type("secbool")
.allowlist_var("sectrue")
.allowlist_var("secfalse")
// storage
.allowlist_var("EXTERNAL_SALT_SIZE")
.allowlist_var("FLAG_PUBLIC")
.allowlist_var("FLAGS_WRITE")
.allowlist_var("MAX_APPID")
.allowlist_type("PIN_UI_WAIT_CALLBACK")
.allowlist_function("storage_init")
.allowlist_function("storage_wipe")
.allowlist_function("storage_is_unlocked")
.allowlist_function("storage_lock")
.allowlist_function("storage_unlock")
.allowlist_function("storage_has_pin")
.allowlist_function("storage_get_pin_rem")
.allowlist_function("storage_change_pin")
.allowlist_function("storage_has")
.allowlist_function("storage_get")
.allowlist_function("storage_set")
.allowlist_function("storage_delete")
.allowlist_function("storage_set_counter")
.allowlist_function("storage_next_counter")
// display
.allowlist_function("display_init")
.allowlist_function("display_refresh")
.allowlist_function("display_backlight")
.allowlist_function("display_text")
.allowlist_function("display_text_width")
.allowlist_function("display_text_height")
.allowlist_function("display_bar")
.allowlist_function("display_bar_radius")
.allowlist_function("display_icon")
.allowlist_function("display_toif_info")
.allowlist_function("display_loader")
.allowlist_function("display_pixeldata")
.allowlist_function("display_pixeldata_dirty")
.allowlist_function("display_set_window")
.allowlist_var("DISPLAY_CMD_ADDRESS")
.allowlist_var("DISPLAY_DATA_ADDRESS")
// bip39
.allowlist_function("mnemonic_word_completion_mask")
.allowlist_var("BIP39_WORDLIST_ENGLISH")
.allowlist_var("BIP39_WORD_COUNT")
// slip39
.allowlist_function("slip39_word_completion_mask")
.allowlist_function("button_sequence_to_word")
// random
.allowlist_function("random_uniform")
// time
.allowlist_function("hal_delay")
.allowlist_function("hal_ticks_ms");
// Write the bindings to a file in the OUR_DIR.
bindings
.generate()
.expect("Unable to generate bindings")
.write_to_file(PathBuf::from(out_path).join("trezorhal.rs"))
.unwrap();
}
fn is_firmware() -> bool {
let target = env::var("TARGET").unwrap();
target.starts_with("thumbv7")
}
#[cfg(feature = "test")]
fn link_core_objects() {
let crate_path = env::var("CARGO_MANIFEST_DIR").unwrap();
let build_path = format!("{}/../../build/unix", crate_path);
// List of object filenames to ignore in the `embed` directory
let embed_blocklist = [OsStr::new("main_main.o")];
// Collect all objects that the `core` library uses, and link it in. We have to
// make sure to avoid the object with the `_main` symbol, so we don't get any
// duplicates.
let mut cc = cc::Build::new();
for obj in glob::glob(&format!("{}/embed/**/*.o", build_path)).unwrap() {
let obj = obj.unwrap();
if embed_blocklist.contains(&obj.file_name().unwrap()) {
// Ignore.
} else {
cc.object(obj);
}
}
for obj in glob::glob(&format!("{}/vendor/**/*.o", build_path)).unwrap() {
let obj = obj.unwrap();
cc.object(obj);
}
// Add frozen modules, if present.
for obj in glob::glob(&format!("{}/*.o", build_path)).unwrap() {
cc.object(obj.unwrap());
}
// Compile all the objects into a static library and link it in automatically.
cc.compile("core_lib");
println!("cargo:rustc-link-lib=SDL2");
println!("cargo:rustc-link-lib=SDL2_image");
}