diff --git a/program/c/makefile b/program/c/makefile index 61b8b1465..204e2e7fd 100644 --- a/program/c/makefile +++ b/program/c/makefile @@ -1,4 +1,6 @@ -OUT_DIR := ./target +# Use the OUT_DIR env variable as the output directory if available, otherwise +# default to ./target +OUT_DIR := $(if $(OUT_DIR),$(OUT_DIR),./target) SOLANA := $(shell dirname $(shell which cargo-build-bpf)) ifneq ("$(wildcard $(SOLANA)/sdk/bpf/c/bpf.mk)","") @@ -9,11 +11,18 @@ else include $(SOLANA)/sdk/sbf/c/sbf.mk endif + +# Bundle C code compiled to bpf for use by rust +.PHONY: cpyth-bpf cpyth-bpf: -# Bundle C code compiled to bpf for use by rust - bash -c "ar rcs target/libcpyth-bpf.a target/**/*.o" + bash -c "ar rcs $(OUT_DIR)/libcpyth-bpf.a target/**/*.o" + + +# 2-Stage Contract Build +# +# 1) Compile C code to system architecture for use by rust's cargo test +# 2) Bundle C code compiled to system architecture for use by rust's cargo test +.PHONY: cpyth-native cpyth-native: -# Compile C code to system architecture for use by rust's cargo test - gcc -c ./src/oracle/native/upd_aggregate.c -o ./target/cpyth-native.o -fPIC -# Bundle C code compiled to system architecture for use by rust's cargo test - ar rcs target/libcpyth-native.a ./target/cpyth-native.o + gcc -c ./src/oracle/native/upd_aggregate.c -o $(OUT_DIR)/cpyth-native.o -fPIC + ar rcs $(OUT_DIR)/libcpyth-native.a $(OUT_DIR)/cpyth-native.o diff --git a/program/rust/Cargo.toml b/program/rust/Cargo.toml index f3bfcf3e0..edf16ee9f 100644 --- a/program/rust/Cargo.toml +++ b/program/rust/Cargo.toml @@ -29,6 +29,7 @@ test-generator = "0.3.1" [features] debug = [] +library = [] [lib] crate-type = ["cdylib", "lib"] diff --git a/program/rust/build.rs b/program/rust/build.rs index d12feb539..3361d3bd5 100644 --- a/program/rust/build.rs +++ b/program/rust/build.rs @@ -7,7 +7,33 @@ use { }; fn main() { - println!("cargo:rustc-link-search=./program/c/target"); + // Cargo exposes ENV variables for feature flags that can be used to determine whether to do a + // self-contained build that compiles the C components automatically. + if std::env::var("CARGO_FEATURE_LIBRARY").is_ok() { + // OUT_DIR is the path cargo provides to a build directory under `target/` specifically for + // isolated build artifacts. We use this to build the C program and then link against the + // resulting static library. This allows building the program when used as a dependency of + // another crate. + let out_dir = std::env::var("OUT_DIR").unwrap(); + let out_dir = PathBuf::from(out_dir); + + // We must forward OUT_DIR as an env variable to the make script otherwise it will output + // its artifacts to the wrong place. + std::process::Command::new("make") + .env("VERBOSE", "1") + .env("OUT_DIR", out_dir.display().to_string()) + .current_dir("../c") + .args(["cpyth-native"]) + .status() + .expect("Failed to build C program"); + + // Emit instructions for cargo to link against the built static library. + println!("cargo:rustc-link-lib=static=cpyth-native"); + println!("cargo:rustc-link-search={}", out_dir.display()); + } else { + // Otherwise fall back to the old relative-path C style build we used to have. + println!("cargo:rustc-link-search=./program/c/target"); + } // Generate and write bindings let bindings = Builder::default() diff --git a/program/rust/src/lib.rs b/program/rust/src/lib.rs index 71fd912d8..da3531d8e 100644 --- a/program/rust/src/lib.rs +++ b/program/rust/src/lib.rs @@ -17,6 +17,25 @@ mod tests; #[cfg(feature = "debug")] mod log; +// When compiled in `library` mode the on-chain definitions provided by this library are +// exported. This is useful when other libraries wish to directly utilise the exact Solana specific +// on-chain definitions of the Pyth program. +// +// While we have `pyth-sdk-rs` which exposes a more friendly interface, this is still useful when a +// downstream user wants to confirm for example that they can compile against the binary interface +// of this program for their specific solana version. +#[cfg(feature = "library")] +pub use accounts::{ + AccountHeader, + MappingAccount, + PermissionAccount, + PriceAccount, + PriceComponent, + PriceEma, + PriceInfo, + ProductAccount, + PythAccount, +}; use { crate::error::OracleError, processor::process_instruction,