use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=guests/shell_guest/src/lib.rs");
println!("cargo:rerun-if-changed=guests/http_guest/src/lib.rs");
println!("cargo:rerun-if-changed=guests/shell_guest/Cargo.toml");
println!("cargo:rerun-if-changed=guests/http_guest/Cargo.toml");
println!("cargo:rerun-if-changed=build.rs");
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
let guests_dir = out_dir.join("guests");
fs::create_dir_all(&guests_dir).expect("Failed to create guests output directory");
if can_compile_guests() {
compile_guests(&guests_dir);
} else {
create_stub_guests(&guests_dir);
}
}
fn can_compile_guests() -> bool {
if env::consts::ARCH != "x86_64" {
println!(
"cargo:warning=Guest compilation skipped: requires x86_64, got {}",
env::consts::ARCH
);
return false;
}
let manifest_dir =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
let guests_dir = manifest_dir.join("guests").join("shell_guest").join("src");
if !guests_dir.exists() {
println!(
"cargo:warning=Guest compilation skipped: guests source not found (crates.io build)"
);
return false;
}
let output = Command::new("rustup")
.args(["target", "list", "--installed"])
.output();
match output {
Ok(output) => {
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.contains("x86_64-unknown-none") {
true
} else {
println!("cargo:warning=Guest compilation skipped: x86_64-unknown-none target not installed");
println!("cargo:warning=Run: rustup target add x86_64-unknown-none");
false
}
}
Err(_) => {
println!("cargo:warning=Guest compilation skipped: rustup not available");
false
}
}
}
fn compile_guests(output_dir: &Path) {
let manifest_dir =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
let guests_workspace = manifest_dir.join("guests");
compile_guest(&guests_workspace, "shell_guest", output_dir);
compile_guest(&guests_workspace, "http_guest", output_dir);
}
fn compile_guest(workspace: &Path, guest_name: &str, output_dir: &Path) {
println!("cargo:warning=Compiling {} guest binary...", guest_name);
let status = Command::new("cargo")
.current_dir(workspace.join(guest_name))
.args(["build", "--release", "--target", "x86_64-unknown-none"])
.env("CARGO_TARGET_DIR", workspace.join("target"))
.status();
match status {
Ok(status) if status.success() => {
let binary_path = workspace
.join("target")
.join("x86_64-unknown-none")
.join("release")
.join(format!("lib{}.a", guest_name));
let dest_path = output_dir.join(guest_name);
if binary_path.exists() {
fs::copy(&binary_path, &dest_path)
.unwrap_or_else(|e| panic!("Failed to copy {} binary: {}", guest_name, e));
println!("cargo:warning={} guest compiled successfully", guest_name);
} else {
let alt_path = workspace
.join("target")
.join("x86_64-unknown-none")
.join("release")
.join(guest_name);
if alt_path.exists() {
fs::copy(&alt_path, &dest_path)
.unwrap_or_else(|e| panic!("Failed to copy {} binary: {}", guest_name, e));
println!("cargo:warning={} guest compiled successfully", guest_name);
} else {
panic!(
"Compiled {} binary not found at {:?} or {:?}",
guest_name, binary_path, alt_path
);
}
}
}
Ok(status) => {
panic!(
"Failed to compile {} guest: exit code {:?}",
guest_name,
status.code()
);
}
Err(e) => {
panic!("Failed to run cargo for {} guest: {}", guest_name, e);
}
}
}
fn create_stub_guests(output_dir: &Path) {
let stub_content = b"STUB_GUEST_BINARY:This is not a valid Hyperlight guest. \
To compile real guests, install the x86_64-unknown-none target: \
rustup target add x86_64-unknown-none";
fs::write(output_dir.join("shell_guest"), stub_content)
.expect("Failed to write stub shell_guest");
fs::write(output_dir.join("http_guest"), stub_content)
.expect("Failed to write stub http_guest");
println!("cargo:warning=Created stub guest binaries (not functional)");
}