elf_magic/
builder.rs

1use crate::domain::{ElfMagicError, SolanaProgram};
2use std::fs;
3use std::path::{Path, PathBuf};
4use std::process::{Command, Stdio};
5
6/// Build multiple Solana programs
7///
8/// Returns the paths to the generated .so files in the same order as input programs.
9pub fn build_programs(
10    cargo_target_dir: &Path,
11    programs: &[SolanaProgram],
12) -> Result<Vec<PathBuf>, ElfMagicError> {
13    programs
14        .iter()
15        .map(|program| build_program(cargo_target_dir, program))
16        .collect()
17}
18
19/// Build a single Solana program using cargo build-sbf
20///
21/// Executes cargo build-sbf on the provided program and returns
22/// the path to the generated .so file.
23pub fn build_program(
24    cargo_target_dir: &Path,
25    program: &SolanaProgram,
26) -> Result<PathBuf, ElfMagicError> {
27    // Create elf-magic subdirectory for our Solana program builds
28    let sbf_out_dir = cargo_target_dir.join("elf-magic-bin");
29
30    // Expected output path for the .so file
31    let program_so_path = sbf_out_dir.join(format!("{}.so", program.name));
32
33    // Remove existing .so file to ensure clean build
34    if program_so_path.exists() {
35        fs::remove_file(&program_so_path).map_err(|e| ElfMagicError::ProgramBuild {
36            program: program.name.clone(),
37            error: format!("Failed to remove existing .so file: {}", e),
38        })?;
39    }
40
41    // Execute cargo build-sbf
42    let status = Command::new("cargo")
43        .args([
44            "build-sbf",
45            "--manifest-path",
46            &program.manifest_path.to_string_lossy(),
47            "--sbf-out-dir",
48            &sbf_out_dir.to_string_lossy(),
49        ])
50        .env(
51            "CARGO_TARGET_DIR", // note cargo-build-sbf doesn't honor CARGO_TARGET_DIR well, but we should set it anyway
52            cargo_target_dir.to_string_lossy().into_owned(),
53        )
54        .stdout(Stdio::inherit())
55        .stderr(Stdio::inherit())
56        .status()
57        .map_err(|e| ElfMagicError::ProgramBuild {
58            program: program.name.clone(),
59            error: format!(
60                "Failed to execute cargo build-sbf: {}\nMake sure solana CLI tools are installed",
61                e
62            ),
63        })?;
64
65    if !status.success() {
66        return Err(ElfMagicError::ProgramBuild {
67            program: program.name.clone(),
68            error: format!("cargo build-sbf failed with exit code: {:?}", status.code()),
69        });
70    }
71
72    // Verify the .so file was created
73    if !program_so_path.exists() {
74        return Err(ElfMagicError::ProgramBuild {
75            program: program.name.clone(),
76            error: format!(
77                "Expected .so file not found at: {}",
78                program_so_path.display()
79            ),
80        });
81    }
82
83    // Set environment variable for this program
84    println!(
85        "cargo:rustc-env={}={}",
86        program.env_var_name(),
87        program_so_path.display()
88    );
89
90    Ok(program_so_path)
91}