use std::{env, process}; use std::path::PathBuf; use bindgen::CargoCallbacks; use std::path::Path; 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 = LIBRARY_PREFIX.to_owned() + "nebula" + LIBRARY_EXTENSION; 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"); 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(LIBRARY_PREFIX.to_owned() + "nebula.h").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!"); } #[cfg(target_family = "unix")] const LIBRARY_EXTENSION: & str = ".a"; #[cfg(target_family = "unix")] const LIBRARY_PREFIX: & str = "lib"; #[cfg(target_family = "windows")] const LIBRARY_EXTENSION: &str = ".dll"; #[cfg(target_family = "windows")] const LIBRARY_PREFIX: &str = ""; 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() } #[cfg(target_family = "unix")] fn print_link() { println!("cargo:rustc-link-lib=static=nebula"); } #[cfg(target_family = "unix")] fn link_type() -> String { "c-archive".to_string() } #[cfg(target_family = "windows")] fn print_link() { println!("cargo:rustc-link-lib=dylib=nebula"); } #[cfg(target_family = "windows")] fn link_type() -> String { "c-shared".to_string() }