Gunnar

nordisk/heimdall

guests/verify-bundle/build.rs

// SPDX-License-Identifier: AGPL-3.0-only
//! Heimdall is a defensive sandbox. With the `openzl` feature on wasm32, compile
//! the vendored OpenZL DECODER subset (openzl/src, see openzl/VENDOR.md) plus
//! openzl/shim/hzl.c into the guest module. Native builds link openzl-sys-rs
//! instead (the same OpenZL 0.3.0 Holger uses) and compile nothing here.
//!
//! The C is built against wasi-libc's HEADERS only (Debian package
//! `wasi-libc`, pinned in ci/ci.Containerfile); no wasi-libc object is linked,
//! so the guest still imports nothing (tests/wasm_e2e.rs proves it).

use std::env;
use std::fs;
use std::path::{Path, PathBuf};

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=openzl");
    println!("cargo:rerun-if-env-changed=HEIMDALL_WASI_INCLUDE");
    let wasm = env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("wasm32");
    if !wasm || env::var_os("CARGO_FEATURE_OPENZL").is_none() {
        return;
    }
    let here = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let v = here.join("openzl/src");
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());

    let wasi_inc = env::var("HEIMDALL_WASI_INCLUDE")
        .map(PathBuf::from)
        .unwrap_or_else(|_| PathBuf::from("/usr/include/wasm32-wasi"));
    assert!(
        wasi_inc.join("stdlib.h").exists(),
        "the `openzl` feature needs wasi-libc's headers for wasm32 at {} (Debian \
         package wasi-libc, see ci/ci.Containerfile; or set HEIMDALL_WASI_INCLUDE), \
         or build with --no-default-features",
        wasi_inc.display()
    );

    // OpenZL's generated config, as openzl-sys-rs writes it.
    let cfg = out.join("include/openzl");
    fs::create_dir_all(&cfg).unwrap();
    fs::write(
        cfg.join("zl_config.h"),
        "#ifndef OPENZL_ZS2_CONFIG_H\n#define OPENZL_ZS2_CONFIG_H\n\
         #define OPENZL_HAVE_X86_64_ASM 0\n#define ZL_ALLOW_INTROSPECTION 0\n\
         #define ZL_HAVE_X86_64_ASM 0\n#endif\n",
    )
    .unwrap();

    let list = fs::read_to_string(here.join("openzl/c-files.txt")).unwrap();
    let files: Vec<&str> = list.lines().filter(|l| !l.is_empty()).collect();
    assert!(files.len() > 100, "openzl/c-files.txt looks truncated");
    let base = |b: &mut cc::Build| {
        b.target("wasm32-unknown-unknown")
            .flag("--target=wasm32-wasip1")
            .flag("-nostdlibinc")
            .flag("-isystem")
            .flag(wasi_inc.to_str().unwrap())
            .opt_level(2)
            .define("NDEBUG", None)
            .flag("-std=gnu11")
            .flag("-ffunction-sections")
            .flag("-fdata-sections")
            .warnings(false);
    };
    let pick = |prefix: &str| -> Vec<PathBuf> {
        files
            .iter()
            .filter(|f| f.starts_with(prefix))
            .map(|f| v.join(f))
            .collect()
    };

    let mut z = cc::Build::new();
    base(&mut z);
    z.include(v.join("deps/zstd/lib"))
        .include(v.join("deps/zstd/lib/common"))
        .define("XXH_NAMESPACE", "ZSTD_")
        .define("ZSTD_LEGACY_SUPPORT", "0")
        .define("ZSTD_DISABLE_ASM", None)
        .files(pick("deps/zstd/"))
        .compile("hzl_zstd");

    let mut l = cc::Build::new();
    base(&mut l);
    l.include(v.join("deps/lz4/lib"))
        .define("XXH_NAMESPACE", "LZ4_")
        .files(pick("deps/lz4/"))
        .compile("hzl_lz4");

    let mut o = cc::Build::new();
    base(&mut o);
    includes(&mut o, &out.join("include"), &v);
    o.define("_GNU_SOURCE", None)
        .files(pick("src/"))
        .file(here.join("openzl/shim/hzl.c"))
        .compile("hzl_openzl");
}

fn includes(b: &mut cc::Build, cfg: &Path, v: &Path) {
    b.include(cfg)
        .include(v)
        .include(v.join("include"))
        .include(v.join("src"))
        .include(v.join("deps/lz4/lib"))
        .include(v.join("deps/zstd/lib"));
}