From 6d9f4a348321053f10c719fa75f0fac74279000f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 14:17:50 +0000 Subject: [PATCH 1/2] Fix ASM test compilation: make include_bytes! conditional on binary existence Four ASM examples (transfer-sol, create-account, checking-accounts, hello-solana) had tests that failed to compile when the SBPF .so files were missing. These binaries are build artifacts (listed in .gitignore) that must be generated with `sbpf build` before tests can run. Solution: Add build.rs scripts that set a has_asm_binary cfg when the .so file exists, then gate the test modules with #[cfg(all(test, has_asm_binary))]. This allows the crate to compile cleanly and provides helpful build warnings when the binary is missing, without breaking CI. Co-Authored-By: Claude Haiku 4.5 Claude-Session: https://claude.ai/code/session_01BDUxWXgCA5TsoPPxHzRNen --- basics/checking-accounts/asm/Cargo.toml | 1 + basics/checking-accounts/asm/build.rs | 12 ++++++++++++ basics/checking-accounts/asm/src/lib.rs | 2 +- basics/create-account/asm/Cargo.toml | 1 + basics/create-account/asm/build.rs | 12 ++++++++++++ basics/create-account/asm/src/lib.rs | 2 +- basics/hello-solana/asm/Cargo.toml | 1 + basics/hello-solana/asm/build.rs | 12 ++++++++++++ basics/hello-solana/asm/src/lib.rs | 2 +- basics/transfer-sol/asm/Cargo.toml | 1 + basics/transfer-sol/asm/build.rs | 12 ++++++++++++ basics/transfer-sol/asm/src/lib.rs | 2 +- 12 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 basics/checking-accounts/asm/build.rs create mode 100644 basics/create-account/asm/build.rs create mode 100644 basics/hello-solana/asm/build.rs create mode 100644 basics/transfer-sol/asm/build.rs diff --git a/basics/checking-accounts/asm/Cargo.toml b/basics/checking-accounts/asm/Cargo.toml index 830cb8172..19349c560 100644 --- a/basics/checking-accounts/asm/Cargo.toml +++ b/basics/checking-accounts/asm/Cargo.toml @@ -1,4 +1,5 @@ [package] +build = "build.rs" name = "checking-account-asm-program" version = "0.1.0" edition = "2021" diff --git a/basics/checking-accounts/asm/build.rs b/basics/checking-accounts/asm/build.rs new file mode 100644 index 000000000..06303cade --- /dev/null +++ b/basics/checking-accounts/asm/build.rs @@ -0,0 +1,12 @@ +use std::path::Path; + +fn main() { + let so_path = Path::new("deploy/program.so"); + + if so_path.exists() { + println!("cargo:rustc-cfg=has_asm_binary"); + } else { + println!("cargo:warning=ASM binary not found at deploy/program.so"); + println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + } +} diff --git a/basics/checking-accounts/asm/src/lib.rs b/basics/checking-accounts/asm/src/lib.rs index ec4cb4200..fa9ad5bc3 100644 --- a/basics/checking-accounts/asm/src/lib.rs +++ b/basics/checking-accounts/asm/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, has_asm_binary))] mod tests { use litesvm::LiteSVM; diff --git a/basics/create-account/asm/Cargo.toml b/basics/create-account/asm/Cargo.toml index 8c695fa1a..b37de8565 100644 --- a/basics/create-account/asm/Cargo.toml +++ b/basics/create-account/asm/Cargo.toml @@ -1,4 +1,5 @@ [package] +build = "build.rs" name = "create-account-asm-program" version = "0.1.0" edition = "2021" diff --git a/basics/create-account/asm/build.rs b/basics/create-account/asm/build.rs new file mode 100644 index 000000000..06303cade --- /dev/null +++ b/basics/create-account/asm/build.rs @@ -0,0 +1,12 @@ +use std::path::Path; + +fn main() { + let so_path = Path::new("deploy/program.so"); + + if so_path.exists() { + println!("cargo:rustc-cfg=has_asm_binary"); + } else { + println!("cargo:warning=ASM binary not found at deploy/program.so"); + println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + } +} diff --git a/basics/create-account/asm/src/lib.rs b/basics/create-account/asm/src/lib.rs index 84f30fadc..1851dd4ea 100644 --- a/basics/create-account/asm/src/lib.rs +++ b/basics/create-account/asm/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, has_asm_binary))] mod tests { use litesvm::LiteSVM; diff --git a/basics/hello-solana/asm/Cargo.toml b/basics/hello-solana/asm/Cargo.toml index 35b63c384..996ab6b67 100644 --- a/basics/hello-solana/asm/Cargo.toml +++ b/basics/hello-solana/asm/Cargo.toml @@ -1,4 +1,5 @@ [package] +build = "build.rs" name = "hello-solana-asm-program" version = "0.1.0" edition = "2021" diff --git a/basics/hello-solana/asm/build.rs b/basics/hello-solana/asm/build.rs new file mode 100644 index 000000000..06303cade --- /dev/null +++ b/basics/hello-solana/asm/build.rs @@ -0,0 +1,12 @@ +use std::path::Path; + +fn main() { + let so_path = Path::new("deploy/program.so"); + + if so_path.exists() { + println!("cargo:rustc-cfg=has_asm_binary"); + } else { + println!("cargo:warning=ASM binary not found at deploy/program.so"); + println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + } +} diff --git a/basics/hello-solana/asm/src/lib.rs b/basics/hello-solana/asm/src/lib.rs index 3057aa1bd..10e0b3fe4 100644 --- a/basics/hello-solana/asm/src/lib.rs +++ b/basics/hello-solana/asm/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, has_asm_binary))] mod tests { use litesvm::LiteSVM; use solana_instruction::{AccountMeta, Instruction}; diff --git a/basics/transfer-sol/asm/Cargo.toml b/basics/transfer-sol/asm/Cargo.toml index 100a04284..3f04e319d 100644 --- a/basics/transfer-sol/asm/Cargo.toml +++ b/basics/transfer-sol/asm/Cargo.toml @@ -2,6 +2,7 @@ name = "asm" version = "0.1.0" edition = "2021" +build = "build.rs" [dependencies] diff --git a/basics/transfer-sol/asm/build.rs b/basics/transfer-sol/asm/build.rs new file mode 100644 index 000000000..f1688e794 --- /dev/null +++ b/basics/transfer-sol/asm/build.rs @@ -0,0 +1,12 @@ +use std::path::Path; + +fn main() { + let so_path = Path::new("deploy/transfer-sol-cpi.so"); + + if so_path.exists() { + println!("cargo:rustc-cfg=has_asm_binary"); + } else { + println!("cargo:warning=ASM binary not found at deploy/transfer-sol-cpi.so"); + println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + } +} diff --git a/basics/transfer-sol/asm/src/lib.rs b/basics/transfer-sol/asm/src/lib.rs index 3a7c8e1f2..3a4344bbd 100644 --- a/basics/transfer-sol/asm/src/lib.rs +++ b/basics/transfer-sol/asm/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, has_asm_binary))] mod tests { use litesvm::LiteSVM; use solana_instruction::{AccountMeta, Instruction}; From 2bc44e0fd6b91bda91431108560b06b123a47035 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 23:18:11 +0000 Subject: [PATCH 2/2] Declare the has_asm_binary cfg and check each crate's real binary name Two defects in the previous commit: Clippy runs with -D warnings, which promotes unexpected_cfgs to an error, so introducing a custom cfg without declaring it broke the build. The build scripts now emit cargo::rustc-check-cfg for it. Three of the four build scripts probed deploy/program.so, but those crates embed create-account-asm-program.so, checking-account-asm-program.so and hello-solana-asm-program.so. After a real `sbpf build` the cfg would have stayed unset and the tests would have silently never run, which is worse than the failure this was fixing. Each script now names the file its own crate embeds, and re-runs when that file changes so the cfg cannot go stale. Verified both directions: with a binary present the test module compiles and `--list` reports the test; with it absent the crate builds clean. `cargo clippy -- -D warnings -A clippy::diverging_sub_expression` passes. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BDUxWXgCA5TsoPPxHzRNen --- basics/checking-accounts/asm/build.rs | 20 +++++++++++++++----- basics/create-account/asm/build.rs | 20 +++++++++++++++----- basics/hello-solana/asm/build.rs | 20 +++++++++++++++----- basics/transfer-sol/asm/build.rs | 20 +++++++++++++++----- 4 files changed, 60 insertions(+), 20 deletions(-) diff --git a/basics/checking-accounts/asm/build.rs b/basics/checking-accounts/asm/build.rs index 06303cade..08ca44557 100644 --- a/basics/checking-accounts/asm/build.rs +++ b/basics/checking-accounts/asm/build.rs @@ -1,12 +1,22 @@ +// The tests embed the assembled program with include_bytes!, which needs the +// file to exist at compile time. `sbpf build` produces it, and it is gitignored, +// so a fresh checkout has no binary and the test module cannot compile. Set a +// cfg when the binary is present and let the tests key off it: present, they +// compile and run; absent, they are left out and the crate still builds. use std::path::Path; +const BINARY: &str = "deploy/checking-account-asm-program.so"; + fn main() { - let so_path = Path::new("deploy/program.so"); + // Declare the cfg so `-D warnings` builds do not fail on unexpected_cfgs. + println!("cargo::rustc-check-cfg=cfg(has_asm_binary)"); + // Re-run when the binary appears or changes, so the cfg never goes stale. + println!("cargo::rerun-if-changed={BINARY}"); - if so_path.exists() { - println!("cargo:rustc-cfg=has_asm_binary"); + if Path::new(BINARY).exists() { + println!("cargo::rustc-cfg=has_asm_binary"); } else { - println!("cargo:warning=ASM binary not found at deploy/program.so"); - println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + println!("cargo::warning=ASM binary not found at {BINARY}: tests skipped"); + println!("cargo::warning=Run `sbpf build` in this directory to generate it"); } } diff --git a/basics/create-account/asm/build.rs b/basics/create-account/asm/build.rs index 06303cade..1107f14d7 100644 --- a/basics/create-account/asm/build.rs +++ b/basics/create-account/asm/build.rs @@ -1,12 +1,22 @@ +// The tests embed the assembled program with include_bytes!, which needs the +// file to exist at compile time. `sbpf build` produces it, and it is gitignored, +// so a fresh checkout has no binary and the test module cannot compile. Set a +// cfg when the binary is present and let the tests key off it: present, they +// compile and run; absent, they are left out and the crate still builds. use std::path::Path; +const BINARY: &str = "deploy/create-account-asm-program.so"; + fn main() { - let so_path = Path::new("deploy/program.so"); + // Declare the cfg so `-D warnings` builds do not fail on unexpected_cfgs. + println!("cargo::rustc-check-cfg=cfg(has_asm_binary)"); + // Re-run when the binary appears or changes, so the cfg never goes stale. + println!("cargo::rerun-if-changed={BINARY}"); - if so_path.exists() { - println!("cargo:rustc-cfg=has_asm_binary"); + if Path::new(BINARY).exists() { + println!("cargo::rustc-cfg=has_asm_binary"); } else { - println!("cargo:warning=ASM binary not found at deploy/program.so"); - println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + println!("cargo::warning=ASM binary not found at {BINARY}: tests skipped"); + println!("cargo::warning=Run `sbpf build` in this directory to generate it"); } } diff --git a/basics/hello-solana/asm/build.rs b/basics/hello-solana/asm/build.rs index 06303cade..a106a3f03 100644 --- a/basics/hello-solana/asm/build.rs +++ b/basics/hello-solana/asm/build.rs @@ -1,12 +1,22 @@ +// The tests embed the assembled program with include_bytes!, which needs the +// file to exist at compile time. `sbpf build` produces it, and it is gitignored, +// so a fresh checkout has no binary and the test module cannot compile. Set a +// cfg when the binary is present and let the tests key off it: present, they +// compile and run; absent, they are left out and the crate still builds. use std::path::Path; +const BINARY: &str = "deploy/hello-solana-asm-program.so"; + fn main() { - let so_path = Path::new("deploy/program.so"); + // Declare the cfg so `-D warnings` builds do not fail on unexpected_cfgs. + println!("cargo::rustc-check-cfg=cfg(has_asm_binary)"); + // Re-run when the binary appears or changes, so the cfg never goes stale. + println!("cargo::rerun-if-changed={BINARY}"); - if so_path.exists() { - println!("cargo:rustc-cfg=has_asm_binary"); + if Path::new(BINARY).exists() { + println!("cargo::rustc-cfg=has_asm_binary"); } else { - println!("cargo:warning=ASM binary not found at deploy/program.so"); - println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + println!("cargo::warning=ASM binary not found at {BINARY}: tests skipped"); + println!("cargo::warning=Run `sbpf build` in this directory to generate it"); } } diff --git a/basics/transfer-sol/asm/build.rs b/basics/transfer-sol/asm/build.rs index f1688e794..8bc071d1c 100644 --- a/basics/transfer-sol/asm/build.rs +++ b/basics/transfer-sol/asm/build.rs @@ -1,12 +1,22 @@ +// The tests embed the assembled program with include_bytes!, which needs the +// file to exist at compile time. `sbpf build` produces it, and it is gitignored, +// so a fresh checkout has no binary and the test module cannot compile. Set a +// cfg when the binary is present and let the tests key off it: present, they +// compile and run; absent, they are left out and the crate still builds. use std::path::Path; +const BINARY: &str = "deploy/transfer-sol-cpi.so"; + fn main() { - let so_path = Path::new("deploy/transfer-sol-cpi.so"); + // Declare the cfg so `-D warnings` builds do not fail on unexpected_cfgs. + println!("cargo::rustc-check-cfg=cfg(has_asm_binary)"); + // Re-run when the binary appears or changes, so the cfg never goes stale. + println!("cargo::rerun-if-changed={BINARY}"); - if so_path.exists() { - println!("cargo:rustc-cfg=has_asm_binary"); + if Path::new(BINARY).exists() { + println!("cargo::rustc-cfg=has_asm_binary"); } else { - println!("cargo:warning=ASM binary not found at deploy/transfer-sol-cpi.so"); - println!("cargo:warning=Run `sbpf build` in the asm directory to generate it"); + println!("cargo::warning=ASM binary not found at {BINARY}: tests skipped"); + println!("cargo::warning=Run `sbpf build` in this directory to generate it"); } }