use std::collections::HashSet;
use std::path::Path;
use std::{fs, io};
use cmake::Config;
fn copy_dir_all(
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
skip: &HashSet<&str>,
) -> io::Result<()> {
fs::create_dir_all(&dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
if ty.is_dir() {
if !skip.contains(&*entry.file_name().to_string_lossy()) {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()), skip)?;
}
} else {
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}
fn cmake_build() -> Result<(), std::io::Error> {
if std::env::var("CARGO_FEATURE_EXTERNAL_CPP").is_ok() {
println!("cargo:warning=MESSAGE Building in development mode");
let root_dir_env = std::env::var("PSP_ROOT_DIR").expect("Must set PSP_ROOT_DIR");
let root_dir = Path::new(root_dir_env.as_str());
copy_dir_all(Path::join(root_dir, "cpp"), "cpp", &HashSet::from(["dist"]))?;
copy_dir_all(Path::join(root_dir, "cmake"), "cmake", &HashSet::new())?;
println!(
"cargo:rerun-if-changed={}/cpp/perspective",
root_dir.display()
);
}
let mut dst = Config::new("cpp/perspective");
let profile = std::env::var("PROFILE").unwrap();
dst.always_configure(true);
dst.define("CMAKE_BUILD_TYPE", profile.as_str());
if std::env::var("PSP_ARCH").as_deref() == Ok("x86_64") {
dst.define("CMAKE_OSX_ARCHITECTURES", "x86_64");
} else if std::env::var("PSP_ARCH").as_deref() == Ok("arm64") {
dst.define("CMAKE_OSX_ARCHITECTURES", "arm64");
}
if std::env::var("TARGET")
.unwrap_or_default()
.contains("wasm32")
{
dst.define("PSP_WASM_BUILD", "1");
} else {
dst.define("PSP_WASM_BUILD", "0");
}
if cfg!(windows) {
dst.define(
"CMAKE_TOOLCHAIN_FILE",
format!(
"{}/scripts/buildsystems/vcpkg.cmake",
std::env::var("VCPKG_ROOT").unwrap().replace("\\", "/")
),
);
}
if std::env::var("CARGO_FEATURE_PYTHON").is_ok() {
dst.define("CMAKE_POSITION_INDEPENDENT_CODE", "ON");
dst.define("PSP_PYTHON_BUILD", "1");
}
if std::env::var("CARGO_FEATURE_EXTERNAL_CPP").is_err() {
dst.env("PSP_DISABLE_CLANGD", "1");
}
if std::env::var("CARGO_FEATURE_WASM_EXCEPTIONS").is_ok() {
dst.define("PSP_WASM_EXCEPTIONS", "1");
} else {
dst.define("PSP_WASM_EXCEPTIONS", "0");
}
if !cfg!(windows) {
dst.build_arg(format!("-j{}", num_cpus::get()));
}
println!("cargo:warning=MESSAGE Building cmake {}", profile);
let artifact = dst.build();
println!("cargo:warning=MESSAGE Building cxx");
let mut compiler = cxx_build::bridge("src/ffi.rs");
compiler
.file("src/server.cpp")
.include("include")
.include("cpp/perspective/src/include")
.std("c++17");
if cfg!(windows) {
compiler.flag_if_supported("/c");
} else {
compiler.static_flag(true);
}
compiler.compile("perspective");
println!(
"cargo:rustc-link-search=native={}/build",
artifact.display()
);
link_cmake_static_archives(artifact.as_path())?;
println!("cargo:rerun-if-changed=cpp/perspective");
println!("cargo:rerun-if-changed=include/server.h");
println!("cargo:rerun-if-changed=src/server.cpp");
println!("cargo:rerun-if-changed=src/lib.rs");
Ok(())
}
fn link_cmake_static_archives(dir: &Path) -> Result<(), std::io::Error> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let path = entry?.path();
if path.is_dir() {
link_cmake_static_archives(&path)?;
} else {
let ext = path.extension().as_ref().map(|x| x.to_string_lossy());
let stem = path.file_stem().as_ref().map(|x| x.to_string_lossy());
let is_archive = (cfg!(windows)
&& ext.as_deref() == Some("lib")
&& stem.as_deref() != Some("perspective"))
|| (!cfg!(windows) && ext.as_deref() == Some("a"));
if is_archive {
let a = if cfg!(windows) {
stem.unwrap().to_string()
} else {
stem.expect("bad")[3..].to_string()
};
println!("cargo:rustc-link-search=native={}", dir.display());
println!("cargo:rustc-link-lib=static={}", a);
}
}
}
}
Ok(())
}
fn main() -> Result<(), std::io::Error> {
cmake_build()
}