use std::env;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
static CMAKE_SEPARATOR: &[u8] = b"\n";
static CMAKE_FILE: &str = "cmake_cryiorust_hdf5";
static BITSHUFFLE_PATCH: &str = "0001-bshuf_h5_filter-guard-comp_lvl-read.patch";
fn main() {
let hdf5_include = PathBuf::from(
env::var("DEP_HDF5_INCLUDE").expect("hdf5-sys did not export its include directory"),
);
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let ext = manifest.join("ext").join("bitshuffle");
let lz4_include = ext.join("lz4");
let src_include = ext.join("src");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let h5filter = patch_h5filter(&ext, &manifest, &out_dir);
write_cmake_file(&[&hdf5_include, &lz4_include, &src_include]);
compile(&ext, &h5filter, &hdf5_include, &lz4_include, &src_include);
}
fn patch_h5filter(ext: &Path, manifest: &Path, out_dir: &Path) -> PathBuf {
let patch = manifest.join("patches").join(BITSHUFFLE_PATCH);
let dir = out_dir.join("src");
fs::create_dir_all(&dir).unwrap();
let dest = dir.join("bshuf_h5filter.c");
fs::copy(ext.join("src").join("bshuf_h5filter.c"), &dest).unwrap();
let output = Command::new("git")
.arg("-C")
.arg(out_dir)
.args(["apply", "-p1"])
.arg(&patch)
.output()
.expect("failed to run git");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"git apply failed for {patch:?}: {stderr}"
);
dest
}
fn compile(
ext: &Path,
h5filter: &Path,
hdf5_include: &Path,
lz4_include: &Path,
src_include: &Path,
) {
let src = [
"lz4/lz4.c",
"src/bitshuffle.c",
"src/bitshuffle_core.c",
"src/bshuf_h5plugin.c",
"src/iochain.c",
];
cc::Build::new()
.files(src.iter().map(|s| ext.join(s)))
.file(h5filter)
.flag("-w")
.include(hdf5_include)
.include(lz4_include)
.include(src_include)
.compile("bitshuffle");
}
fn write_cmake_file(includes: &[&Path]) {
let mut file = File::create(profile_dir().join(CMAKE_FILE)).unwrap();
for (n, include) in includes.iter().enumerate() {
if n > 0 {
file.write_all(CMAKE_SEPARATOR).unwrap();
}
file.write_all(include.to_str().unwrap().as_bytes())
.unwrap();
}
}
fn profile_dir() -> PathBuf {
let out = env::var("OUT_DIR").unwrap();
let mut dir = Path::new(&out);
while let Some(parent) = dir.parent() {
if dir.file_name().is_some_and(|name| name == "build") {
return parent.to_path_buf();
}
dir = parent;
}
panic!("cannot locate the cargo profile directory from OUT_DIR {out}");
}