use bindgen::CargoCallbacks; use std::path::Path; use std::path::PathBuf; use std::{env, process}; fn get_cargo_target_dir() -> Result> { let skip_triple = std::env::var("TARGET")? == std::env::var("HOST")?; let skip_parent_dirs = if skip_triple { 4 } else { 5 }; let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?); let mut current = out_dir.as_path(); for _ in 0..skip_parent_dirs { current = current.parent().ok_or("not found")?; } Ok(std::path::PathBuf::from(current)) } fn main() { // Find compiler: // 1. GOC // 2. /usr/local/go/bin/go // 3. system "go" let compiler = match env::var("GOC") { Ok(c) => c, Err(_) => { if Path::new("/usr/local/go/bin/go").exists() { "/usr/local/go/bin/go".to_string() } else { "go".to_string() } } }; println!("using go compiler {}", compiler); //gobuild::Build::new().compiler(compiler).buildmode(BuildMode::CArchive).file("main.go").compile("nebulaffi"); let c_compiler = cc::Build::new().try_get_compiler().unwrap(); let out_dir = env::var("OUT_DIR").unwrap(); let out_path = PathBuf::from(out_dir); let out_file = lib_name(); let out = out_path.join(out_file); let mut command = process::Command::new(compiler); command.args([ "build", "-buildmode", link_type().as_str(), "-o", out.display().to_string().as_str(), "main.go", ]); command.env("CGO_ENABLED", "1"); command.env("CC", c_compiler.path()); command.env("GOARCH", goarch()); command.env("GOOS", goos()); println!("running go compile command: {:?}", command); let mut child = command.spawn().unwrap(); let status = child.wait().unwrap(); println!("{}", status); if !status.success() { panic!("`{:?}` exited with status code {}", command, status); } println!("Go compile success"); copy_if_windows(); print_link(); println!( "cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap() ); //let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); //println!("cargo:rustc-link-search={}", out_path.display()); println!("cargo:rerun-if-changed=go.mod"); println!("cargo:rerun-if-changed=go.sum"); println!("cargo:rerun-if-changed=main.go"); println!("Generating bindings"); let bindings = bindgen::Builder::default() .header(out_path.join(header_name()).display().to_string()) .parse_callbacks(Box::new(CargoCallbacks)) .generate() .expect("Error generating CFFI bindings"); bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); } fn lib_name() -> String { if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" { "nebula.dll".to_string() } else { "libnebula.a".to_string() } } fn header_name() -> String { if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" { "nebula.h".to_string() } else { "libnebula.h".to_string() } } fn copy_if_windows() { let target_dir = get_cargo_target_dir().unwrap(); let target_file = target_dir.join(lib_name()); let out_dir = env::var("OUT_DIR").unwrap(); let out_path = PathBuf::from(out_dir); let out_file = lib_name(); let out = out_path.join(out_file); std::fs::copy(out, target_file).unwrap(); } fn goarch() -> String { match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { "x86" => "386", "x86_64" => "amd64", "mips" => "mips", "powerpc" => "ppc", "powerpc64" => "ppc64", "arm" => "arm", "aarch64" => "arm64", arch => panic!("unsupported architecture {arch}"), } .to_string() } fn goos() -> String { match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { "windows" => "windows", "macos" => "darwin", "ios" => "darwin", "linux" => "linux", "android" => "android", "freebsd" => "freebsd", "dragonfly" => "dragonfly", "openbsd" => "openbsd", "netbsd" => "netbsd", os => panic!("unsupported operating system {os}"), } .to_string() } fn print_link() { if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" { println!("cargo:rustc-link-lib=dylib=nebula"); } else { println!("cargo:rustc-link-lib=static=nebula"); } } fn link_type() -> String { if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" { "c-shared".to_string() } else { "c-archive".to_string() } }