use std::mem::take; use std::process::{Command, Stdio}; #[cfg(not(any(feature = "Lua5_3", feature = "Lua5_4")))] compile_error!("must specify feature \"Lua5_3\" or \"Lua5_4\""); #[cfg(all(feature = "Lua5_3", feature = "Lua5_4"))] compile_error!("features \"Lua5_3\" and \"Lua5_4\" are currently mutually exclusive and cannot be enabled at the same time"); fn main() { let mut include_dirs: Vec = Vec::new(); let mut lib_dirs: Vec = Vec::new(); let mut lib_names: Vec = Vec::new(); // use `pkg-config` binary to determine Lua library name and location { #[cfg(feature = "Lua5_3")] const PKG_NAME: &'static str = "lua-5.3"; #[cfg(feature = "Lua5_4")] const PKG_NAME: &'static str = "lua-5.4"; #[cfg(not(any(feature = "Lua5_3", feature = "Lua5_4")))] const PKG_NAME: &'static str = unreachable!(); let mut pkgcnf_cmd = Command::new("pkg-config"); pkgcnf_cmd .args(["--cflags", "--libs", PKG_NAME]) .stderr(Stdio::inherit()); eprintln!("using pkg-config command: {pkgcnf_cmd:?}"); let pkgcnf = pkgcnf_cmd.output().expect("could not execute pkg-config"); eprintln!("pkg-config status: {:?}", pkgcnf.status); eprintln!( "pkg-config stdout: {:?}", String::from_utf8_lossy(&pkgcnf.stdout) ); if !pkgcnf.status.success() { panic!("pkg-config returned with failure"); } let mut parse_element = |s: String| { if s.len() >= 2 { let prefix = &s[0..2]; let value = &s[2..]; match prefix { "-I" => include_dirs.push(value.to_string()), "-L" => lib_dirs.push(value.to_string()), "-l" => lib_names.push(value.to_string()), _ => (), } } }; let mut element: String = Default::default(); let mut escape: bool = false; for ch in String::from_utf8(pkgcnf.stdout) .expect("invalid UTF-8 from pkg-config") .chars() { if escape { element.push(ch); escape = false; } else if ch == '\\' { escape = true; } else if ch.is_ascii_whitespace() { parse_element(take(&mut element)); } else { element.push(ch); } } if escape { panic!("unexpected EOF from pkg-config (escape character at end)"); } parse_element(element); if lib_names.is_empty() { panic!("pkg-config did not return any library name"); } } // create automatic bindings { let mut builder = bindgen::Builder::default(); for dir in &include_dirs { builder = builder.clang_arg(format!("-I{dir}")); } builder = builder.header("src/cmach.c"); builder = builder.parse_callbacks(Box::new(bindgen::CargoCallbacks)); let bindings = builder.generate().expect("unable to generate bindings"); let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("ffi_cmach.rs")) .expect("unable to write bindings"); } // build own C lib { println!("cargo:rerun-if-changed=src/cmach.c"); let mut config = cc::Build::new(); for dir in &include_dirs { config.include(dir); } config.file("src/cmach.c"); config.compile("libffi_cmach.a"); } // link with Lua for dir in &lib_dirs { println!("cargo:rustc-link-search=native={}", dir); } for name in &lib_names { println!("cargo:rustc-link-lib={}", name); } }