use std::path::PathBuf;
use anyhow::Result;
pub mod config;
pub mod detection;
mod riscv_builder;
mod solana_builder;
pub mod toolchain;
pub use config::{BuildFileConfig, BuildType, CompileFlags, RiscvConfig, SourceType};
pub use detection::{detect_program_type, ProgramType};
pub use riscv_builder::RiscvBuilder;
pub use solana_builder::SolanaBuilder;
pub use toolchain::{
BuildSystemConfig, DownloadSource, GnuRiscvToolchain, RialoRustToolchain, RustSourceBuilder,
S3StorageBackend, SourceBuildConfig, SourceBuildable, Toolchain, ToolchainConfig,
ToolchainType,
};
#[derive(Debug, Clone)]
pub struct BuildConfig {
pub program_path: PathBuf,
pub output_dir: PathBuf,
pub target_dir: PathBuf,
}
pub fn validate_program_path(path: &std::path::Path) -> Result<()> {
if !path.exists() {
return Err(anyhow::anyhow!(
"Program path does not exist: {}",
path.display()
));
}
if !path.is_dir() {
return Err(anyhow::anyhow!(
"Program path is not a directory: {}",
path.display()
));
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RiscvTarget {
Rv32i,
Rv32im,
Rv64gc,
#[default]
RialoCustom,
}
impl RiscvTarget {
pub fn as_target_triple(&self) -> &str {
match self {
RiscvTarget::Rv32i => "riscv32i-unknown-none-elf",
RiscvTarget::Rv32im => "riscv32im-unknown-none-elf",
RiscvTarget::Rv64gc => "riscv64gc-unknown-none-elf",
RiscvTarget::RialoCustom => "riscv64emac-solana-solana",
}
}
pub fn as_march(&self) -> &str {
match self {
RiscvTarget::Rv32i => "rv32i",
RiscvTarget::Rv32im => "rv32im",
RiscvTarget::Rv64gc => "rv64gc",
RiscvTarget::RialoCustom => "rv64gc", }
}
pub fn as_mabi(&self) -> &str {
match self {
RiscvTarget::Rv32i | RiscvTarget::Rv32im => "ilp32",
RiscvTarget::Rv64gc | RiscvTarget::RialoCustom => "lp64d",
}
}
pub fn requires_rialo_toolchain(&self) -> bool {
matches!(self, RiscvTarget::RialoCustom)
}
}
#[derive(Debug, Clone)]
pub enum BuilderConfig {
Solana {},
Riscv {
toolchain_version: Option<String>,
target: RiscvTarget,
},
}
#[derive(Debug, serde::Serialize)]
pub struct BuildResult {
pub package_name: String,
pub output_dir: PathBuf,
pub program_binary: PathBuf,
#[serde(skip_serializing_if = "Option::is_none")]
pub program_keypair: Option<PathBuf>,
}
pub trait ProgramBuilder {
fn validate(&self) -> Result<()>;
fn build(&self, config: &BuildConfig) -> Result<BuildResult>;
}
pub fn create_builder(builder_config: &BuilderConfig) -> Result<Box<dyn ProgramBuilder>> {
match builder_config {
BuilderConfig::Solana {} => Ok(Box::new(SolanaBuilder::default())),
BuilderConfig::Riscv {
toolchain_version,
target,
} => {
let builder = if let Some(version) = toolchain_version {
RiscvBuilder::with_version(version, *target)?
} else {
RiscvBuilder::new(*target)?
};
Ok(Box::new(builder))
}
}
}
pub fn build_program(config: &BuildConfig) -> Result<BuildResult> {
let builder = create_builder(&BuilderConfig::Solana {})?;
builder.validate()?;
builder.build(config)
}
pub fn auto_detect_builder(program_path: &std::path::Path) -> Result<BuilderConfig> {
let file_config = BuildFileConfig::from_directory(program_path)?;
if let Some(config) = &file_config {
if let Some(build_type) = config.build_type {
match build_type {
BuildType::Solana => return Ok(BuilderConfig::Solana {}),
BuildType::Riscv => {
let target = config
.riscv
.as_ref()
.and_then(|r| r.target)
.unwrap_or_default();
let toolchain_version = config
.riscv
.as_ref()
.and_then(|r| r.toolchain_version.clone());
return Ok(BuilderConfig::Riscv {
toolchain_version,
target,
});
}
BuildType::Auto => {
}
}
}
}
let program_type = detect_program_type(program_path)?;
match program_type {
ProgramType::Solana => Ok(BuilderConfig::Solana {}),
ProgramType::RiscvC | ProgramType::RiscvRust => {
let target = file_config
.as_ref()
.and_then(|c| c.riscv.as_ref())
.and_then(|r| r.target)
.unwrap_or_default();
let toolchain_version = file_config
.as_ref()
.and_then(|c| c.riscv.as_ref())
.and_then(|r| r.toolchain_version.clone());
Ok(BuilderConfig::Riscv {
toolchain_version,
target,
})
}
}
}
pub fn build_program_auto(config: &BuildConfig) -> Result<BuildResult> {
let builder_config = auto_detect_builder(&config.program_path)?;
let builder = create_builder(&builder_config)?;
builder.validate()?;
builder.build(config)
}