use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
fn build_gvproxy(source_dir: &Path, output_path: &Path) {
println!("cargo:warning=Building libgvproxy from Go sources...");
let download_status = Command::new("go")
.args(["mod", "download"])
.current_dir(source_dir)
.status()
.expect("Failed to run 'go mod download' - ensure Go is installed");
if !download_status.success() {
panic!("Failed to download Go module dependencies");
}
let mut build_cmd = Command::new("go");
build_cmd.args(["build", "-buildmode=c-shared"]);
#[cfg(target_os = "macos")]
build_cmd.arg("-ldflags=-extldflags=-headerpad_max_install_names");
build_cmd.args([
"-o",
output_path.to_str().expect("Invalid output path"),
"main.go",
"stats.go",
]);
let build_status = build_cmd
.current_dir(source_dir)
.status()
.expect("Failed to run 'go build' - ensure Go is installed");
if !build_status.success() {
panic!("Failed to build libgvproxy");
}
println!("cargo:warning=Successfully built libgvproxy");
}
#[cfg(target_os = "macos")]
fn fix_install_name(lib_name: &str, lib_path: &Path) {
let lib_path_str = lib_path.to_str().expect("Invalid library path");
let status = Command::new("install_name_tool")
.args(["-id", &format!("@rpath/{}", lib_name), lib_path_str])
.status()
.expect("Failed to execute install_name_tool");
if !status.success() {
panic!("Failed to set install_name for libgvproxy");
}
}
#[cfg(target_os = "linux")]
fn fix_install_name(lib_name: &str, lib_path: &Path) {
let lib_path_str = lib_path.to_str().expect("Invalid library path");
let status = Command::new("patchelf")
.args([
"--set-soname",
lib_name, lib_path_str,
])
.status()
.expect("Failed to execute patchelf");
if !status.success() {
panic!("Failed to set install_name for libgvproxy");
}
}
fn get_library_name() -> &'static str {
if cfg!(target_os = "macos") {
"libgvproxy.dylib"
} else if cfg!(target_os = "linux") {
"libgvproxy.so"
} else if cfg!(target_os = "windows") {
"libgvproxy.dll"
} else {
panic!("Unsupported platform for libgvproxy");
}
}
fn auto_detect_registry() {
if env::var("BOXLITE_DEPS_STUB").is_err() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
if manifest_dir.join(".cargo_vcs_info.json").exists() {
env::set_var("BOXLITE_DEPS_STUB", "2");
}
}
}
fn main() {
println!("cargo:rerun-if-changed=gvproxy-bridge/main.go");
println!("cargo:rerun-if-changed=gvproxy-bridge/stats.go");
println!("cargo:rerun-if-changed=gvproxy-bridge/go.mod");
println!("cargo:rerun-if-env-changed=BOXLITE_DEPS_STUB");
auto_detect_registry();
if env::var("BOXLITE_DEPS_STUB").is_ok() {
println!("cargo:warning=BOXLITE_DEPS_STUB mode: skipping libgvproxy build");
println!("cargo:rustc-link-lib=dylib=gvproxy");
println!("cargo:LIBGVPROXY_BOXLITE_DEP=/nonexistent");
return;
}
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
let source_dir = Path::new(&manifest_dir).join("gvproxy-bridge");
let lib_name = get_library_name();
let lib_output = Path::new(&out_dir).join(lib_name);
build_gvproxy(&source_dir, &lib_output);
fix_install_name(lib_name, &lib_output);
let header_src = source_dir.join("libgvproxy.h");
if header_src.exists() {
let header_dst = Path::new(&out_dir).join("libgvproxy.h");
fs::copy(&header_src, &header_dst).expect("Failed to copy libgvproxy.h");
}
println!("cargo:rustc-link-search=native={}", out_dir);
println!("cargo:rustc-link-lib=dylib=gvproxy");
println!("cargo:LIBGVPROXY_BOXLITE_DEP={}", out_dir);
}