Skip to content

Borsch traits using bindgen #196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions program/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
23 changes: 23 additions & 0 deletions program/rust/build.rs
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think people usually put a space after the // and before the text. Actually I'm kind of surprised that rustfmt doesn't fix this for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, sorry noob Rust developer here. Forgot to run cargo fmt. Once this and the other PR are approved, I will do it in a seperate PR that is just fmt. (and then run it after every commit).

If I run it now the diffs will probably be confusing for reviewers.

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!");
}
35 changes: 35 additions & 0 deletions program/rust/build_utils.rs
Original file line number Diff line number Diff line change
@@ -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<String>>,
}

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<String>) {
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<String> {
self.types_to_traits
.get(_name)
.unwrap_or(&Vec::<String>::new())
.to_vec()
}
}
11 changes: 11 additions & 0 deletions program/rust/src/c_oracle_header.rs
Original file line number Diff line number Diff line change
@@ -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");
19 changes: 17 additions & 2 deletions program/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙇 for writing documentation explaining how this works.


//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")]
Expand Down
2 changes: 0 additions & 2 deletions scripts/build-bpf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down