Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 67 additions & 46 deletions crates/c-api/build.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,76 @@
use std::env;
use std::process::Command;

// WASMTIME_FEATURE_LIST
const FEATURES: &[&str] = &[
"ASYNC",
"PROFILING",
"CACHE",
"PARALLEL_COMPILATION",
"WASI",
"WASI_HTTP",
"LOGGING",
"DISABLE_LOGGING",
"COREDUMP",
"ADDR2LINE",
"DEMANGLE",
"THREADS",
"GC",
"GC_DRC",
"GC_NULL",
"CRANELIFT",
"WINCH",
"DEBUG_BUILTINS",
"WAT",
"POOLING_ALLOCATOR",
"COMPONENT_MODEL",
"COMPONENT_MODEL_ASYNC",
"PULLEY",
"ALL_ARCH",
];
// ... if you add a line above this be sure to change the other locations
// marked WASMTIME_FEATURE_LIST
use std::fs;
use std::path::{Path, PathBuf};

fn main() {
println!("cargo:rerun-if-changed=cmake/features.cmake");
println!("cargo:rerun-if-changed=cmake/install-headers.cmake");
println!("cargo:rerun-if-changed=include");

let out_dir = std::env::var("OUT_DIR").unwrap();
let mut cmake = Command::new("cmake");
cmake.arg("-DWASMTIME_DISABLE_ALL_FEATURES=ON");
cmake.arg(format!("-DCMAKE_INSTALL_PREFIX={out_dir}"));
for f in FEATURES {
if env::var_os(format!("CARGO_FEATURE_{f}")).is_some() {
cmake.arg(format!("-DWASMTIME_FEATURE_{f}=ON"));
}
}
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let include_src = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("include");
let dst = out_dir.join("include");

cmake.arg("-P").arg("cmake/install-headers.cmake");
generate_conf_header(&include_src, &dst);
copy_headers(&include_src, &dst);

println!("cargo:include={}", dst.display());
}

let status = cmake.status().expect("failed to spawn `cmake`");
assert!(status.success());
/// Generates `wasmtime/conf.h` from `wasmtime/conf.h.in`, mirroring what
/// `cmake/install-headers.cmake` does for standalone CMake builds: each
/// `#cmakedefine WASMTIME_FEATURE_X` line becomes `#define WASMTIME_FEATURE_X`
/// when the corresponding Cargo feature is enabled and `/* #undef ... */`
/// otherwise. The feature list is read from the template itself, so this
/// function needs no copy of the WASMTIME_FEATURE_LIST.
///
/// Note the CRLF line endings, matching cmake's `NEWLINE_STYLE CRLF` so that
/// the generated header is byte-identical either way.
fn generate_conf_header(include_src: &Path, dst: &Path) {
let template_path = include_src.join("wasmtime").join("conf.h.in");
let template = fs::read_to_string(&template_path)
.unwrap_or_else(|e| panic!("failed to read {template_path:?}: {e}"));
let mut conf = String::new();
for line in template.lines() {
if let Some(rest) = line.strip_prefix("#cmakedefine ") {
let var = rest.split_whitespace().next().unwrap();
let feature = var
.strip_prefix("WASMTIME_FEATURE_")
.unwrap_or_else(|| panic!("unexpected #cmakedefine {var} in conf.h.in"));
if env::var_os(format!("CARGO_FEATURE_{feature}")).is_some() {
conf.push_str("#define ");
conf.push_str(var);
} else {
conf.push_str("/* #undef ");
conf.push_str(var);
conf.push_str(" */");
}
} else {
conf.push_str(line);
}
conf.push_str("\r\n");
}
let conf_dir = dst.join("wasmtime");
fs::create_dir_all(&conf_dir).unwrap();
fs::write(conf_dir.join("conf.h"), conf).unwrap();
}

println!("cargo:include={out_dir}/include");
/// Copies all `.h`/`.hh` files under `include/` into `$OUT_DIR/include`,
/// preserving the directory structure (the equivalent of cmake's
/// `file(INSTALL ... FILES_MATCHING REGEX "\.hh?$")`).
fn copy_headers(src: &Path, dst: &Path) {
for entry in fs::read_dir(src).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
copy_headers(&path, &dst.join(entry.file_name()));
} else {
let is_header = path
.extension()
.map(|e| e == "h" || e == "hh")
.unwrap_or(false);
if is_header {
fs::create_dir_all(dst).unwrap();
fs::copy(&path, dst.join(entry.file_name())).unwrap();
}
}
}
}
Loading