Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/knut

xtask/src/main.rs

//! Workspace task runner (pure Rust, zero shell).
//!
//! Usage: `cargo run -p xtask -- <task>`. Tasks:
//!
//! * `build-wasm-guest` — build the real `wasm32-unknown-unknown` guest in
//!   `crates/knut-bifrost/wasm-guest/` (excluded from the native workspace), so
//!   the `#[ignore]`d `knut-bifrost` `tests/wasm_guest.rs` has its artifact to
//!   load. This is the CI/local step that gate depends on (TEST-GAP-2).
//!
//! The nornir bench suite lives in `examples/nornir-bench.rs` — run it with
//! `cargo bench -p xtask` or `nornir bench run`.

use std::path::{Path, PathBuf};
use std::process::Command;

use anyhow::{Context, Result, bail};

fn main() -> Result<()> {
    let task = std::env::args().nth(1);
    match task.as_deref() {
        Some("build-wasm-guest") => build_wasm_guest(),
        Some(other) => {
            eprintln!("unknown task `{other}`");
            print_usage();
            std::process::exit(2);
        }
        None => {
            print_usage();
            Ok(())
        }
    }
}

fn print_usage() {
    eprintln!("knut xtask — workspace task runner");
    eprintln!();
    eprintln!("tasks:");
    eprintln!("  build-wasm-guest   build the wasm32 guest for knut-bifrost's WasmTransform host");
    eprintln!();
    eprintln!("(the nornir bench suite is `cargo bench -p xtask`)");
}

/// The workspace root — the parent of this `xtask` crate's manifest dir.
fn workspace_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .expect("xtask is a workspace member with a parent")
        .to_path_buf()
}

/// The `cargo` binary to invoke — honour `$CARGO` (set when we ourselves were
/// launched by cargo) so the toolchain stays consistent, else fall back to
/// `cargo` on `PATH`.
fn cargo_bin() -> String {
    std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
}

/// Build the `knut-bifrost-wasm-guest` crate for `wasm32-unknown-unknown`
/// (release), leaving the artifact where `tests/wasm_guest.rs` expects it:
/// `crates/knut-bifrost/wasm-guest/target/wasm32-unknown-unknown/release/knut_bifrost_wasm_guest.wasm`.
///
/// The guest is excluded from the native workspace (it only builds for wasm32),
/// so this drives it out-of-line via its own manifest. The `wasm32` target is
/// ensured best-effort first (`rustup target add`); if `rustup` is absent we
/// press on and let the build surface a clear error.
fn build_wasm_guest() -> Result<()> {
    const TARGET: &str = "wasm32-unknown-unknown";
    let root = workspace_root();
    let manifest = root.join("crates/knut-bifrost/wasm-guest/Cargo.toml");
    if !manifest.exists() {
        bail!("wasm-guest manifest not found at {}", manifest.display());
    }

    ensure_wasm_target(TARGET);

    eprintln!("xtask: building knut-bifrost-wasm-guest for {TARGET} (release)…");
    let status = Command::new(cargo_bin())
        .args([
            "build",
            "-p",
            "knut-bifrost-wasm-guest",
            "--target",
            TARGET,
            "--release",
            "--manifest-path",
        ])
        .arg(&manifest)
        .status()
        .context("failed to spawn cargo for the wasm-guest build")?;
    if !status.success() {
        bail!(
            "wasm-guest build failed (exit {}); is the `{TARGET}` target installed? \
             `rustup target add {TARGET}`",
            status.code().unwrap_or(-1)
        );
    }

    // The guest carries an empty `[workspace]` table, so its target dir is under
    // its own crate dir, not the root target/.
    let artifact = manifest
        .parent()
        .unwrap()
        .join("target")
        .join(TARGET)
        .join("release")
        .join("knut_bifrost_wasm_guest.wasm");
    verify_artifact(&artifact)?;
    eprintln!("xtask: wasm guest built at {}", artifact.display());
    eprintln!(
        "xtask: run the gated test with:\n  {} test -p knut-bifrost --features wasm --test wasm_guest -- --ignored",
        cargo_bin()
    );
    Ok(())
}

/// Best-effort `rustup target add <target>`; a missing `rustup` (or an already
/// installed target) is not fatal — the subsequent build reports the real error.
fn ensure_wasm_target(target: &str) {
    match Command::new("rustup")
        .args(["target", "add", target])
        .status()
    {
        Ok(s) if s.success() => {}
        Ok(_) => eprintln!("xtask: `rustup target add {target}` did not succeed; continuing"),
        Err(_) => eprintln!("xtask: `rustup` not found; assuming `{target}` is available"),
    }
}

/// Confirm the built artifact exists and is non-empty.
fn verify_artifact(path: &Path) -> Result<()> {
    let meta = std::fs::metadata(path)
        .with_context(|| format!("wasm artifact missing after build: {}", path.display()))?;
    if meta.len() == 0 {
        bail!("wasm artifact is empty: {}", path.display());
    }
    Ok(())
}