use std::env;
use std::path::PathBuf;
use std::process::Command;
fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let libloong_root = if manifest_dir.join("_vendor").exists() {
println!("cargo:warning=Using vendored sources from _vendor/");
manifest_dir.join("_vendor")
} else if let Some(parent) = manifest_dir.parent() {
if parent.join("CMakeLists.txt").exists() && parent.join("lib").exists() {
parent.to_path_buf()
} else {
panic!(
"Cannot find libloong sources. \n\
For publishing: run ./vendor-sources.sh first\n\
For development: ensure this is in the libloong repository"
);
}
} else {
panic!("Cannot determine libloong root directory");
};
println!("cargo:rerun-if-changed=wrapper/libloong_wrapper.cpp");
println!("cargo:rerun-if-changed=wrapper/libloong_wrapper.h");
if !manifest_dir.join("_vendor").exists() {
println!("cargo:rerun-if-changed=../lib/libloong");
println!("cargo:rerun-if-changed=../CMakeLists.txt");
}
let build_dir = out_dir.join("libloong_build");
std::fs::create_dir_all(&build_dir).expect("Failed to create build directory");
let cmake_available = Command::new("cmake").arg("--version").output().is_ok();
if !cmake_available {
panic!("CMake is required to build libloong. Please install CMake and try again.");
}
let cmake_cache = build_dir.join("CMakeCache.txt");
if cmake_cache.exists() {
if let Ok(cache_content) = std::fs::read_to_string(&cmake_cache) {
let expected_source = libloong_root.canonicalize().ok();
let mut cache_is_stale = false;
for line in cache_content.lines() {
if line.starts_with("CMAKE_HOME_DIRECTORY:") {
if let Some(cached_dir) = line.split('=').nth(1) {
let cached_path = PathBuf::from(cached_dir).canonicalize().ok();
if cached_path != expected_source {
println!("cargo:warning=Detected stale CMake cache (source dir changed), cleaning build directory");
cache_is_stale = true;
break;
}
}
}
}
if cache_is_stale {
std::fs::remove_dir_all(&build_dir).ok();
std::fs::create_dir_all(&build_dir).expect("Failed to recreate build directory");
}
}
}
let mut cmake_config = Command::new("cmake");
cmake_config
.current_dir(&build_dir)
.arg(&libloong_root)
.arg("-DCMAKE_BUILD_TYPE=Release");
let is_docs_rs = env::var("DOCS_RS").is_ok();
if is_docs_rs {
println!("cargo:warning=Building for docs.rs: disabling binary translation");
cmake_config.arg("-DLA_BINARY_TRANSLATION=OFF");
} else {
cmake_config.arg("-DLA_BINARY_TRANSLATION=ON");
}
if Command::new("ninja").arg("--version").output().is_ok() {
cmake_config.arg("-GNinja");
}
let status = cmake_config
.status()
.expect("Failed to run CMake configuration");
if !status.success() {
panic!("CMake configuration failed");
}
let status = Command::new("cmake")
.current_dir(&build_dir)
.arg("--build")
.arg(".")
.arg("--target")
.arg("loong")
.arg("--config")
.arg("Release")
.arg("--parallel")
.status()
.expect("Failed to build libloong");
if !status.success() {
panic!("Failed to build libloong C++ library");
}
let mut build = cc::Build::new();
build
.cpp(true)
.std("c++20")
.include(libloong_root.join("lib"))
.include(build_dir.join("lib")) .file("wrapper/libloong_wrapper.cpp");
if !cfg!(debug_assertions) {
build.opt_level(2);
}
build.compile("loong_wrapper");
println!(
"cargo:rustc-link-search=native={}",
build_dir.join("lib").display()
);
println!("cargo:rustc-link-lib=static=loong");
let target = env::var("TARGET").unwrap();
if target.contains("apple") {
println!("cargo:rustc-link-lib=c++");
} else if target.contains("linux") {
println!("cargo:rustc-link-lib=stdc++");
} else if target.contains("windows") {
println!("cargo:rustc-link-lib=msvcrt");
}
}