diff --git a/program/rust/Cargo.toml b/program/rust/Cargo.toml index 91b35559f..782b40b6f 100644 --- a/program/rust/Cargo.toml +++ b/program/rust/Cargo.toml @@ -7,8 +7,13 @@ edition = "2021" license = "Apache 2.0" publish = false +[build-dependencies] +bindgen = "0.60.1" + [dependencies] solana-program = "=1.10.29" +borsh = "0.9" + [lib] crate-type = ["cdylib", "lib"] diff --git a/program/rust/build.rs b/program/rust/build.rs index 60da04a8d..e12cf262e 100644 --- a/program/rust/build.rs +++ b/program/rust/build.rs @@ -1,3 +1,26 @@ +mod build_utils; +use bindgen::Builder; +use std::vec::Vec; + fn main() { println!("cargo:rustc-link-search=../c/target"); + + let borsh_derives = ["BorshSerialize".to_string(), "BorshDeserialize".to_string()]; + + //make a parser and to it type, traits pairs + let mut parser = build_utils::DeriveAdderParserCallback::new(); + parser.register_traits("cmd_hdr", borsh_derives.to_vec()); + parser.register_traits("pc_acc", borsh_derives.to_vec()); + + + //generate and write bindings + let bindings = Builder::default() + .header("./src/bindings.h") + .parse_callbacks(Box::new(parser)) + .rustfmt_bindings(true) + .generate() + .expect("Unable to generate bindings"); + bindings + .write_to_file("./bindings.rs") + .expect("Couldn't write bindings!"); } diff --git a/program/rust/build_utils.rs b/program/rust/build_utils.rs new file mode 100644 index 000000000..7848d73a3 --- /dev/null +++ b/program/rust/build_utils.rs @@ -0,0 +1,35 @@ +use bindgen::callbacks::ParseCallbacks; +use std::collections::HashMap; +use std::panic::UnwindSafe; +///This type stores a hashmap from structnames +///to vectors of trait names, and ensures +///that the traits of each struct are added to its +///definition when an instance of this struct +///is provided as a ParseCallback for bindgen +#[derive(Debug, Default)] +pub struct DeriveAdderParserCallback<'a> { + pub types_to_traits: HashMap<&'a str, Vec>, +} + +impl<'a> DeriveAdderParserCallback<'a> { + ///create a parser that does not add any traits + pub fn new() -> Self { + Default::default() + } + //add pairs of types and their desired traits + pub fn register_traits(&mut self, type_name: &'a str, traits: Vec) { + self.types_to_traits.insert(&type_name, traits); + } +} + +//this is required to implement the callback trait +impl UnwindSafe for DeriveAdderParserCallback<'_> {} + +impl ParseCallbacks for DeriveAdderParserCallback<'_> { + fn add_derives(&self, _name: &str) -> Vec { + self.types_to_traits + .get(_name) + .unwrap_or(&Vec::::new()) + .to_vec() + } +} diff --git a/program/rust/src/c_oracle_header.rs b/program/rust/src/c_oracle_header.rs new file mode 100644 index 000000000..5432e3e7b --- /dev/null +++ b/program/rust/src/c_oracle_header.rs @@ -0,0 +1,11 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +//we do not use all the variables in oracle.h, so this helps with the warnings +#![allow(unused_variables)] +#![allow(dead_code)] +//All the custom trait imports should go here +use borsh::{BorshDeserialize, BorshSerialize}; +//bindings.rs is generated by build.rs to include +//things defined in bindings.h +include!("../bindings.rs"); diff --git a/program/rust/src/lib.rs b/program/rust/src/lib.rs index 27c2ba0f5..c4ebbc1c8 100644 --- a/program/rust/src/lib.rs +++ b/program/rust/src/lib.rs @@ -1,8 +1,23 @@ -//c_oracle_header is auto generated by build_bpf.sh -//to reflect the current status of oracle.h mod c_oracle_header; mod time_machine_types; +//Below is a high lever description of the rust/c setup. + +//As we migrate from C to Rust, our Rust code needs to be able to interract with C +//build-bpf.sh is set up to compile the C code into a bpf archive file, that build.rs +//is set up to link the rust targets to. This enables to interact with the c_entrypoint +//as well as similarly declare other C functions in Rust and call them + +//We also generate bindings for the types and constants in oracle.h (as well as other things +//included in bindings.h), these bindings can be accessed through c_oracle_header.rs +//Bindings allow us to access type definitions, function definitions and constants. In order to +//add traits to the bindings, we use the parser in build.rs. The traits must be defined/included +//at the the top of c_oracle_headers.rs. One of the most important traits we deal are the Borsh +//serialization traits. + +//the only limitation of our set up is that we can not unit test in rust, anything that calls +//a c function. Though we can test functions that use constants/types defined in oracle.h + //do not link with C during unit tests (which are built in native architecture, unlike libpyth.o) #[cfg(target_arch = "bpf")] #[link(name = "cpyth")] diff --git a/scripts/build-bpf.sh b/scripts/build-bpf.sh index 95818a2ad..8880ce140 100755 --- a/scripts/build-bpf.sh +++ b/scripts/build-bpf.sh @@ -40,8 +40,6 @@ rm ./target/*-keypair.json #build Rust and link it with C cd "${RUST_DIR}" -cargo install bindgen -bindgen ./src/bindings.h -o ./src/c_oracle_header.rs cargo clean cargo test cargo clean