// 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"));
}