Skip to main content

build_javm/
lib.rs

1//! build.rs helper: cross-compile a guest crate and emit a JAVM
2//! `Image` blob.
3//!
4//! The cross-compile is [`nub_build::pvm2::build_elf`]; the JAVM part
5//! is the last two lines — wrap the linked program in the cap shape and
6//! SSZ-encode it. Consumers `include_bytes!` the returned path.
7
8use std::path::PathBuf;
9
10use ssz::Encode;
11
12/// Emit `cargo:rerun-if-changed` for the cap-wrapping sources, so the
13/// blob is rebuilt when the Image format changes. `nub_build::pvm2`
14/// watches the linker and program-format sources on its own.
15fn watch_transpiler_sources() {
16    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
17    let crates_dir = PathBuf::from(&manifest_dir)
18        .parent()
19        .expect("build-javm must be inside rust/")
20        .to_path_buf();
21
22    build_crate::emit_rerun_for_dir(&crates_dir.join("javm-transpiler/src"));
23}
24
25/// Build a JAVM `Image` blob from a guest crate, returning the path to
26/// the SSZ-encoded `.pvm` file in `OUT_DIR`.
27///
28/// `Image::code` holds raw RV+C+custom-0 bytes, consumed directly by
29/// the recompiler / interpreter.
30pub fn build(manifest_dir: &str, bin_name: &str) -> PathBuf {
31    watch_transpiler_sources();
32    let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR not set");
33    let blob_path = PathBuf::from(&out_dir).join(format!("{bin_name}.pvm"));
34
35    if std::env::var("SKIP_GUEST_BUILD").is_ok() {
36        if !blob_path.exists() {
37            std::fs::write(&blob_path, b"").ok();
38        }
39        return blob_path;
40    }
41
42    let elf_path = nub_build::pvm2::build_elf(manifest_dir, bin_name);
43    let elf_data = std::fs::read(&elf_path).expect("failed to read ELF");
44    let image = javm_transpiler::linker::link_elf(&elf_data).expect("failed to link ELF to Image");
45
46    std::fs::write(&blob_path, image.as_ssz_bytes()).expect("failed to write Image blob");
47    blob_path
48}