use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=bridge.go");
println!("cargo:rerun-if-changed=bridge.h");
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set by cargo"));
let bridge_dir = PathBuf::from(".");
let target_triple = env::var("TARGET")
.unwrap_or_else(|_| env::var("HOST").expect("Neither TARGET nor HOST set by cargo"));
let is_windows = target_triple.contains("windows");
let lib_filename = if is_windows {
"libcue_bridge.lib"
} else {
"libcue_bridge.a"
};
let output_path = out_dir.join(lib_filename);
let header_path = out_dir.join("libcue_bridge.h");
println!("=== CUENGINE BUILD SCRIPT DEBUG ===");
println!("Building for target: {target_triple}");
println!("Is Windows: {is_windows}");
println!("Expected library: {}", output_path.display());
println!("Bridge directory: {}", bridge_dir.display());
println!("Out directory: {}", out_dir.display());
println!(
"Bridge GO file exists: {}",
bridge_dir.join("bridge.go").exists()
);
match std::process::Command::new("go").arg("version").output() {
Ok(output) if output.status.success() => {
println!(
"Go version: {}",
String::from_utf8_lossy(&output.stdout).trim()
);
}
Ok(_) => println!("Go command failed"),
Err(e) => println!("Go not available: {e}"),
}
let workspace_root =
PathBuf::from(env::var("CARGO_WORKSPACE_DIR").unwrap_or_else(|_| "../..".to_string()));
if !try_use_prebuilt(
lib_filename,
&bridge_dir,
&workspace_root,
&output_path,
&header_path,
) {
build_go_bridge(&bridge_dir, &output_path);
assert!(
output_path.exists(),
"Go bridge library was not created at expected path: {}",
output_path.display()
);
println!("Successfully created library at: {}", output_path.display());
}
configure_rustc_linking(&target_triple, &out_dir);
}
fn try_use_prebuilt(
lib_filename: &str,
bridge_dir: &Path,
workspace_root: &Path,
output_path: &PathBuf,
header_path: &PathBuf,
) -> bool {
let prebuilt_locations = [
(
workspace_root.join("target/debug").join(lib_filename),
workspace_root.join("target/debug/libcue_bridge.h"),
),
(
workspace_root.join("target/release").join(lib_filename),
workspace_root.join("target/release/libcue_bridge.h"),
),
(
bridge_dir.join("target/debug").join(lib_filename),
bridge_dir.join("target/debug/libcue_bridge.h"),
),
(
bridge_dir.join("target/release").join(lib_filename),
bridge_dir.join("target/release/libcue_bridge.h"),
),
(
PathBuf::from(env::var("CUE_BRIDGE_PATH").unwrap_or_default())
.join("debug")
.join(lib_filename),
PathBuf::from(env::var("CUE_BRIDGE_PATH").unwrap_or_default())
.join("debug/libcue_bridge.h"),
),
(
PathBuf::from(env::var("CUE_BRIDGE_PATH").unwrap_or_default())
.join("release")
.join(lib_filename),
PathBuf::from(env::var("CUE_BRIDGE_PATH").unwrap_or_default())
.join("release/libcue_bridge.h"),
),
];
for (lib_path, header_path_candidate) in &prebuilt_locations {
if lib_path.is_file()
&& header_path_candidate.is_file()
&& !lib_path.to_string_lossy().is_empty()
{
let _ = std::fs::remove_file(output_path);
let _ = std::fs::remove_file(header_path);
std::fs::copy(lib_path, output_path).unwrap_or_else(|e| {
panic!(
"Failed to copy pre-built bridge from {}: {}",
lib_path.display(),
e
)
});
std::fs::copy(header_path_candidate, header_path).unwrap_or_else(|e| {
panic!(
"Failed to copy pre-built header from {}: {}",
header_path_candidate.display(),
e
)
});
let build_type = if lib_path.to_string_lossy().contains("release") {
"release"
} else {
"debug"
};
println!(
"Using pre-built Go bridge ({}) from: {}",
build_type,
lib_path.display()
);
return true;
}
}
false
}
fn build_go_bridge(bridge_dir: &Path, output_path: &Path) {
println!("Building Go bridge from source");
let mut cmd = Command::new("go");
cmd.current_dir(bridge_dir).arg("build");
#[cfg(target_os = "macos")]
{
cmd.env("MACOSX_DEPLOYMENT_TARGET", "11.0");
cmd.env("CGO_CFLAGS", "-mmacosx-version-min=11.0");
cmd.env("CGO_LDFLAGS", "-mmacosx-version-min=11.0");
}
if bridge_dir.join("vendor").exists() {
cmd.arg("-mod=vendor");
println!("Using vendor directory");
}
let output_str = output_path
.to_str()
.expect("Failed to convert output path to string");
cmd.args(["-buildmode=c-archive", "-o", output_str, "bridge.go"]);
println!("Running Go command: {cmd:?}");
let output = cmd
.output()
.expect("Failed to execute Go command. Make sure Go is installed.");
if !output.status.success() {
println!("Go build failed!");
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
panic!("Failed to build libcue bridge");
}
println!("Go build completed successfully");
}
fn configure_rustc_linking(target_triple: &str, out_dir: &Path) {
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=cue_bridge");
if target_triple.contains("windows") {
println!("cargo:rustc-link-lib=ws2_32");
println!("cargo:rustc-link-lib=userenv");
println!("cargo:rustc-link-lib=ntdll");
println!("cargo:rustc-link-lib=winmm");
println!("cargo:rustc-link-lib=legacy_stdio_definitions");
} else {
println!("cargo:rustc-link-lib=pthread");
println!("cargo:rustc-link-lib=m");
println!("cargo:rustc-link-lib=dl");
if target_triple.contains("apple") || target_triple.contains("darwin") {
println!("cargo:rustc-link-lib=framework=Security");
println!("cargo:rustc-link-lib=framework=CoreFoundation");
println!("cargo:rustc-link-lib=framework=SystemConfiguration");
}
}
}