use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let ws_root = manifest_dir.parent().unwrap();
let out_dir = ws_root.join("out");
let groups = collect_groups(&out_dir);
if groups.is_empty() {
println!(
"cargo:warning=No Zig projects found under {}/out/ — run `cargo run -p js2rustc` first",
ws_root.display()
);
return;
}
let mut lib_dirs = Vec::new();
for group in &groups {
let zig_project_dir = out_dir.join(group);
println!(
"cargo:rerun-if-changed={}",
zig_project_dir.join("src").to_string_lossy()
);
println!(
"cargo:rerun-if-changed={}",
zig_project_dir.join("cabi_exports.json").to_string_lossy()
);
let status = Command::new("zig")
.args(["build", "-Doptimize=ReleaseSafe"])
.current_dir(&zig_project_dir)
.status();
match status {
Ok(s) if s.success() => {
if let Some(lib_dir) = find_static_lib_dir(&zig_project_dir, group) {
lib_dirs.push(lib_dir);
}
}
Ok(_) => {
eprintln!(
"cargo:warning=zig build failed for group '{}'",
group
);
}
Err(e) => {
eprintln!(
"cargo:warning=Failed to run zig build for group '{}': {}",
group, e
);
}
}
}
let mut emitted_dirs = std::collections::HashSet::new();
for lib_dir in &lib_dirs {
let lib_dir_str = lib_dir.to_string_lossy().to_string();
if emitted_dirs.insert(lib_dir_str.clone()) {
println!("cargo:rustc-link-search=native={}", lib_dir_str);
}
}
for group in &groups {
println!("cargo:rustc-link-lib=static={group}");
}
println!("cargo:rustc-link-lib=ntdll");
}
fn collect_groups(out_dir: &Path) -> Vec<String> {
let mut groups = Vec::new();
if let Ok(entries) = fs::read_dir(out_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir()
&& path.join("build.zig").exists()
&& let Some(name) = path.file_name().and_then(|n| n.to_str())
{
groups.push(name.to_string());
}
}
}
groups
}
fn find_static_lib_dir(zig_project_dir: &Path, lib_name: &str) -> Option<PathBuf> {
let lib_filename = format!("{lib_name}.lib");
let zig_out = zig_project_dir.join("zig-out").join("lib");
if zig_out.is_dir() && has_static_lib(&zig_out, &lib_filename) {
return Some(zig_out);
}
let zig_cache = zig_project_dir.join(".zig-cache").join("lib");
if zig_cache.is_dir() && has_static_lib(&zig_cache, &lib_filename) {
return Some(zig_cache);
}
search_for_static_lib(zig_project_dir, &lib_filename)
}
fn has_static_lib(dir: &Path, filename: &str) -> bool {
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
if entry.file_name().to_string_lossy() == filename {
return true;
}
}
}
false
}
fn search_for_static_lib(dir: &Path, filename: &str) -> Option<PathBuf> {
if !dir.is_dir() {
return None;
}
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
if let Some(result) = search_for_static_lib(&path, filename) {
return Some(result);
}
} else if path.file_name().and_then(|n| n.to_str()) == Some(filename) {
return path.parent().map(|p| p.to_path_buf());
}
}
}
None
}