diff --git a/sdk/cargo-build-bpf/.gitignore b/sdk/cargo-build-bpf/.gitignore new file mode 100644 index 0000000000..1686a357aa --- /dev/null +++ b/sdk/cargo-build-bpf/.gitignore @@ -0,0 +1,2 @@ +/tests/crates/*/target/ +Cargo.lock diff --git a/sdk/cargo-build-bpf/src/main.rs b/sdk/cargo-build-bpf/src/main.rs index 3e177422b0..b07f349d2c 100644 --- a/sdk/cargo-build-bpf/src/main.rs +++ b/sdk/cargo-build-bpf/src/main.rs @@ -39,7 +39,8 @@ impl Default for Config { .parent() .expect("Unable to get parent directory") .to_path_buf() - .join("sdk/bpf"), + .join("sdk") + .join("bpf"), bpf_out_dir: None, dump: false, features: vec![], @@ -592,6 +593,13 @@ fn main() { let matches = App::new(crate_name!()) .about(crate_description!()) .version(crate_version!()) + .arg( + Arg::with_name("bpf_out_dir") + .long("bpf-out-dir") + .value_name("DIRECTORY") + .takes_value(true) + .help("Place final BPF build artifacts in this directory"), + ) .arg( Arg::with_name("bpf_sdk") .long("bpf-sdk") @@ -600,12 +608,39 @@ fn main() { .default_value(&default_bpf_sdk) .help("Path to the Solana BPF SDK"), ) + .arg( + Arg::with_name("cargo_args") + .help("Arguments passed directly to `cargo build`") + .multiple(true) + .last(true), + ) .arg( Arg::with_name("dump") .long("dump") .takes_value(false) .help("Dump ELF information to a text file on success"), ) + .arg( + Arg::with_name("features") + .long("features") + .value_name("FEATURES") + .takes_value(true) + .multiple(true) + .help("Space-separated list of features to activate"), + ) + .arg( + Arg::with_name("manifest_path") + .long("manifest-path") + .value_name("PATH") + .takes_value(true) + .help("Path to Cargo.toml"), + ) + .arg( + Arg::with_name("no_default_features") + .long("no-default-features") + .takes_value(false) + .help("Do not activate the `default` feature"), + ) .arg( Arg::with_name("offline") .long("offline") @@ -619,34 +654,6 @@ fn main() { .takes_value(false) .help("Use verbose output"), ) - .arg( - Arg::with_name("features") - .long("features") - .value_name("FEATURES") - .takes_value(true) - .multiple(true) - .help("Space-separated list of features to activate"), - ) - .arg( - Arg::with_name("no_default_features") - .long("no-default-features") - .takes_value(false) - .help("Do not activate the `default` feature"), - ) - .arg( - Arg::with_name("manifest_path") - .long("manifest-path") - .value_name("PATH") - .takes_value(true) - .help("Path to Cargo.toml"), - ) - .arg( - Arg::with_name("bpf_out_dir") - .long("bpf-out-dir") - .value_name("DIRECTORY") - .takes_value(true) - .help("Place final BPF build artifacts in this directory"), - ) .arg( Arg::with_name("workspace") .long("workspace") diff --git a/sdk/cargo-build-bpf/tests/crates.rs b/sdk/cargo-build-bpf/tests/crates.rs new file mode 100644 index 0000000000..2dac22a000 --- /dev/null +++ b/sdk/cargo-build-bpf/tests/crates.rs @@ -0,0 +1,70 @@ +use std::{ + env, fs, + io::{self, Write}, + process::{Command, Output}, +}; + +fn run_cargo_build(extra_args: &[&str]) -> Output { + let cwd = env::current_dir().expect("Unable to get current working directory"); + let root = cwd + .parent() + .expect("Unable to get parent directory of current working dir") + .parent() + .expect("Unable to get ../.. of current working dir"); + let toml = cwd + .join("tests") + .join("crates") + .join("noop") + .join("Cargo.toml"); + let toml = format!("{}", toml.display()); + let mut args = vec!["--bpf-sdk", "../bpf", "--manifest-path", &toml]; + for arg in extra_args { + args.push(arg); + } + let cargo_build_bpf = root.join("target").join("debug").join("cargo-build-bpf"); + Command::new(cargo_build_bpf) + .args(&args) + .output() + .expect("Error running cargo-build-bpf") +} + +#[test] +fn test_build() { + let output = run_cargo_build(&[]); + assert!(output.status.success()); +} + +// This test requires rustfilt. +// TODO: Add a check for rustfilt, and install it if not available. +#[ignore] +#[test] +fn test_dump() { + let output = run_cargo_build(&["--dump"]); + if !output.status.success() { + eprintln!("--- stdout ---"); + io::stderr().write_all(&output.stdout).unwrap(); + eprintln!("--- stderr ---"); + io::stderr().write_all(&output.stderr).unwrap(); + eprintln!("--------------"); + } + assert!(output.status.success()); + let cwd = env::current_dir().expect("Unable to get current working directory"); + let dump = cwd + .join("tests") + .join("crates") + .join("noop") + .join("target") + .join("deploy") + .join("noop-dump.txt"); + assert!(dump.exists()); +} + +#[test] +fn test_out_dir() { + let output = run_cargo_build(&["--bpf-out-dir", "tmp_out"]); + assert!(output.status.success()); + let cwd = env::current_dir().expect("Unable to get current working directory"); + let dir = cwd.join("tmp_out"); + assert!(dir.exists()); + fs::remove_dir_all("tmp_out").expect("Failed to remove tmp_out dir"); +} diff --git a/sdk/cargo-build-bpf/tests/crates/noop/Cargo.toml b/sdk/cargo-build-bpf/tests/crates/noop/Cargo.toml new file mode 100644 index 0000000000..d3f5b2c1d4 --- /dev/null +++ b/sdk/cargo-build-bpf/tests/crates/noop/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "noop" +version = "1.7.12" +description = "Solana BPF test program written in Rust" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +edition = "2018" +publish = false + +[dependencies] +solana-program = { path = "../../../../program", version = "=1.7.12" } + +[lib] +crate-type = ["cdylib"] + +[workspace] diff --git a/sdk/cargo-build-bpf/tests/crates/noop/src/lib.rs b/sdk/cargo-build-bpf/tests/crates/noop/src/lib.rs new file mode 100644 index 0000000000..ac6c3d6c84 --- /dev/null +++ b/sdk/cargo-build-bpf/tests/crates/noop/src/lib.rs @@ -0,0 +1,15 @@ +//! @brief Example Rust-based BPF noop program + +extern crate solana_program; +use solana_program::{ + account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, +}; + +entrypoint!(process_instruction); +fn process_instruction( + _program_id: &Pubkey, + _accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { + Ok(()) +}