weavy 0.2.2

Shared lowered-program substrate for interpreters and copy-and-patch backends.
Documentation
#[cfg(feature = "jit")]
use copypatch::extract::{Stencil, compile_object, extract_stencil};
#[cfg(feature = "jit")]
use std::{
    env, fs,
    path::{Path, PathBuf},
};

#[cfg(feature = "jit")]
const SYMBOLS: &[&str] = &["weavy_stencil_hostcall", "weavy_stencil_done"];

fn main() {
    #[cfg(feature = "jit")]
    {
        let out = PathBuf::from(env::var("OUT_DIR").unwrap());
        let generated = out.join("weavy_stencils.rs");
        let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
        let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();

        if native_copy_patch_target(&target_os, &target_arch) {
            emit_native(&out, &generated);
        } else {
            emit_empty(&generated);
        }
    }
}

#[cfg(feature = "jit")]
fn emit_empty(generated: &Path) {
    fs::write(
        generated,
        "pub const HOSTCALL: &[u8] = &[];\n\
         pub const HOSTCALL_CONT: &[usize] = &[];\n\
         pub const DONE: &[u8] = &[];\n",
    )
    .unwrap();
}

#[cfg(feature = "jit")]
fn native_copy_patch_target(target_os: &str, target_arch: &str) -> bool {
    matches!(
        (target_os, target_arch),
        ("macos", "aarch64") | ("linux", "x86_64")
    )
}

#[cfg(feature = "jit")]
fn emit_native(out: &Path, generated: &Path) {
    println!("cargo:rerun-if-changed=stencils/hostcall.rs");
    println!("cargo:rerun-if-changed=build.rs");

    let target = env::var("TARGET").expect("TARGET set by cargo");
    let obj = out.join("weavy_stencils.o");
    let src = Path::new("stencils/hostcall.rs");

    let rustc = env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
    assert!(
        compile_object(&rustc, &[], src, &obj, &target, false),
        "rustc failed to compile Weavy stencils"
    );

    let bytes = fs::read(&obj).unwrap();
    let hostcall = extract_stencil(&bytes, SYMBOLS, "weavy_stencil_hostcall", "weavy_cont");
    let done = extract_stencil(&bytes, SYMBOLS, "weavy_stencil_done", "weavy_cont");

    let mut s = String::new();
    s.push_str("// Generated by build.rs from stencils/hostcall.rs (rustc --emit=obj, call).\n");
    emit(
        &mut s,
        "HOSTCALL",
        "`weavy_stencil_hostcall`: call one consumer-provided intrinsic, continue.",
        &hostcall,
    );
    emit_cont(&mut s, "HOSTCALL_CONT", "HOSTCALL", &hostcall);
    emit(&mut s, "DONE", "`weavy_stencil_done`: a lone `ret`.", &done);
    fs::write(generated, s).unwrap();
}

#[cfg(feature = "jit")]
fn emit(out: &mut String, name: &str, doc: &str, stencil: &Stencil) {
    out.push_str(&format!(
        "/// {doc}\npub const {name}: &[u8] = &{:?};\n",
        stencil.bytes
    ));
}

#[cfg(feature = "jit")]
fn emit_cont(out: &mut String, name: &str, of: &str, stencil: &Stencil) {
    out.push_str(&format!(
        "/// Byte offsets within `{of}` of the continuation relocations to patch.\n\
         pub const {name}: &[usize] = &{:?};\n",
        stencil.cont_relocs
    ));
}