use std::{env, fs, io};
use std::path::{Path,PathBuf};
use std::process::Command;
use walkdir::WalkDir;
fn build_cmake_backend_project(backend_source_path: &PathBuf, build_dir_path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
let out_dir = env::var("OUT_DIR").expect("OUT_DIR environment variable not set");
let build_path_temp = Path::new(&out_dir).join("./build_temp");
let cmake_build = Command::new("cmake")
.arg("-S")
.arg(&backend_source_path)
.arg("-B")
.arg(&build_path_temp)
.arg("-DCMAKE_BUILD_TYPE=RelWithDebInfo")
.output()?;
if !cmake_build.status.success() {
return Err(format!("Failed to generate build files: {}", cmake_build.status).into());
}
let cmake_output = Command::new("cmake")
.arg("--build")
.arg(build_path_temp.clone())
.arg("--parallel")
.arg("--config")
.arg("Release")
.output()?;
if !cmake_output.status.success() {
return Err(format!("Failed to build project: {}", cmake_output.status).into());
}
copy_prebuilt_c_lib(&build_path_temp, build_dir_path).expect("Failed to copy prebuilt C library files");
fs::remove_dir_all(&build_path_temp).map_err(|err| format!("Error removing temporary build directory: {}", err))?;
Ok(())
}
fn copy_prebuilt_c_lib(temp_build_path: &PathBuf, final_build_path: &PathBuf) -> io::Result<()> {
if ! final_build_path.exists() {
fs::create_dir_all(&final_build_path)?;
}
let walker = WalkDir::new(temp_build_path).follow_links(true);
for entry in walker {
let entry = entry?;
let file_type = entry.file_type();
if file_type.is_file() {
let src_path = entry.path();
let file_name = src_path.file_name().unwrap().to_str().unwrap();
if file_name.ends_with(".dll") || file_name.ends_with(".so") || file_name.ends_with(".dylib") || file_name.ends_with(".metal") {
#[cfg(not(target_os = "macos"))]
{
if file_name.ends_with(".metal") { continue; }
}
let dest_path = final_build_path.join(file_name);
fs::copy(&src_path, &dest_path)?;
}
}
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let backend_source_path = if Path::new("./gpt4all-backend").exists() {
PathBuf::from("./gpt4all-backend")
} else {
PathBuf::from("./../../gpt4all-backend")
};
let out_dir_path = PathBuf::from(env::var("OUT_DIR")?);
let build_dir_path = out_dir_path.join("build");
build_cmake_backend_project(&backend_source_path, &build_dir_path)?;
let canonized_lib_folder = build_dir_path
.canonicalize()?;
#[cfg(target_os = "linux")]
{
let symlink_path = canonized_lib_folder.join("libllmodel.so.0");
let target_path = canonized_lib_folder.join("libllmodel.so");
std::os::unix::fs::symlink(target_path, symlink_path)?;
}
println!("cargo:rustc-link-search=native={}", canonized_lib_folder.display());
println!("cargo:rustc-link-lib={}", "llmodel");
Ok(())
}