#![allow(clippy::unwrap_used)]
use std::{fs, path::PathBuf, process::Command};
pub enum BlobType {
Service,
Authorizer,
}
impl BlobType {
pub fn dispatch_table(&self) -> Vec<Vec<u8>> {
match self {
Self::Service =>
vec![b"refine_ext".into(), b"accumulate_ext".into(), b"on_transfer_ext".into()],
Self::Authorizer => vec![b"is_authorized_ext".into()],
}
}
}
pub fn build_service(crate_dir: PathBuf, crate_name: &str) {
let out_dir: PathBuf = std::env::var("OUT_DIR").expect("No OUT_DIR").into();
let output_file = out_dir.join(format!("{}.jam", &crate_name));
println!("cargo:rerun-if-changed={}", crate_dir.to_str().unwrap());
build_pvm_blob(crate_dir, crate_name, BlobType::Service, &output_file, &out_dir);
}
pub fn build_authorizer(crate_dir: PathBuf, crate_name: &str) {
let out_dir: PathBuf = std::env::var("OUT_DIR").expect("No OUT_DIR").into();
let output_file = out_dir.join(format!("{}.jam", &crate_name));
println!("cargo:rerun-if-changed={}", crate_dir.to_str().unwrap());
build_pvm_blob(crate_dir, crate_name, BlobType::Authorizer, &output_file, &out_dir);
}
pub fn build_pvm_blob(
crate_dir: PathBuf,
crate_name: &str,
blob_type: BlobType,
output_file: &PathBuf,
out_dir: &PathBuf,
) {
let is_64_bit = cfg!(feature = "use-64-bit-pvm");
let (target_name, target_json_path) = if is_64_bit {
("riscv64emac-unknown-none-polkavm", polkavm_linker::target_json_64_path().unwrap())
} else {
("riscv32emac-unknown-none-polkavm", polkavm_linker::target_json_32_path().unwrap())
};
let build_res = Command::new("cargo")
.current_dir(&crate_dir)
.env_clear()
.env("PATH", std::env::var("PATH").unwrap())
.env("RUSTFLAGS", "-C panic=abort")
.env("CARGO_TARGET_DIR", &out_dir)
.args(["build", "-Z", "build-std=core,alloc", "--release", "--target"])
.arg(target_json_path)
.output()
.expect("Failed to execute rustup process");
if !build_res.status.success() {
let stderr = String::from_utf8_lossy(&build_res.stderr);
eprintln!("{}", stderr);
std::process::exit(1);
}
let mut config = polkavm_linker::Config::default();
config.set_strip(true);
config.set_dispatch_table(blob_type.dispatch_table());
let input_path = &out_dir.join(target_name).join("release").join(crate_name);
let orig =
fs::read(input_path).unwrap_or_else(|e| panic!("Failed to read {:?} :{:?}", input_path, e));
let linked = polkavm_linker::program_from_elf(config, orig.as_ref())
.expect("Failed to link polkavm program:");
let output_path_polkavm = &out_dir.join(format!("{}.polkavm", &crate_name));
fs::write(output_path_polkavm, &linked).expect("Error writing resulting binary");
let parts = polkavm_linker::ProgramParts::from_bytes(linked.into())
.expect("failed to deserialize linked PolkaVM program");
let mut ro_data = parts.ro_data.to_vec();
ro_data.resize(parts.ro_data_size as usize, 0);
let blob_jam = jam_types::ProgramBlob {
ro_data: ro_data.into(),
rw_data: (&parts.rw_data[..]).into(),
code_blob: (&parts.code_and_jump_table[..]).into(),
rw_data_padding: (parts.rw_data_size as usize - parts.rw_data.len()) as u32,
stack_size: parts.stack_size,
};
fs::write(output_file, blob_jam.to_vec().expect("error serializing the .jam blob"))
.expect("error writing the .jam blob");
}
#[macro_export]
macro_rules! pvm_binary {
($name:literal) => {
include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".jam"));
};
}