use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
const MODULES: [&str; 5] = ["common", "hll", "cpc", "theta", "tuple"];
const IGNORED: [&str; 1] = ["version.hpp.in"];
fn collect(dir: &Path) -> BTreeMap<PathBuf, Vec<u8>> {
let mut out = BTreeMap::new();
let mut stack = vec![dir.to_path_buf()];
while let Some(current) = stack.pop() {
let entries = std::fs::read_dir(¤t)
.unwrap_or_else(|e| panic!("cannot read {}: {e}", current.display()));
for entry in entries {
let path = entry.expect("cannot read directory entry").path();
if path.is_dir() {
stack.push(path);
continue;
}
let name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or_default();
if IGNORED.contains(&name) {
continue;
}
let relative = path
.strip_prefix(dir)
.expect("walked path is under dir")
.to_path_buf();
let bytes = std::fs::read(&path)
.unwrap_or_else(|e| panic!("cannot read {}: {e}", path.display()));
out.insert(relative, bytes);
}
}
out
}
#[test]
fn vendored_headers_match_the_pinned_submodule() {
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
let vendored_root = manifest.join("vendor/datasketches-cpp");
let submodule_root = manifest
.parent()
.expect("the sys crate has a parent directory")
.join("vendor/datasketches-cpp");
if !submodule_root.join("common/include").is_dir() {
eprintln!(
"skipping: no submodule checkout at {} \
(run `git submodule update --init --recursive` to enable this check)",
submodule_root.display()
);
return;
}
let mut problems = Vec::new();
for module in MODULES {
let vendored = vendored_root.join(module).join("include");
let upstream = submodule_root.join(module).join("include");
if !vendored.is_dir() {
problems.push(format!("{module}: missing from the vendored copy entirely"));
continue;
}
if !upstream.is_dir() {
problems.push(format!("{module}: missing from the submodule entirely"));
continue;
}
let vendored_files = collect(&vendored);
let upstream_files = collect(&upstream);
for path in upstream_files.keys() {
if !vendored_files.contains_key(path) {
problems.push(format!(
"{module}: {} is missing from the vendored copy (upstream added it?)",
path.display()
));
}
}
for path in vendored_files.keys() {
if !upstream_files.contains_key(path) {
problems.push(format!(
"{module}: {} exists only in the vendored copy (upstream removed it?)",
path.display()
));
}
}
for (path, upstream_bytes) in &upstream_files {
if let Some(vendored_bytes) = vendored_files.get(path) {
if vendored_bytes != upstream_bytes {
problems.push(format!("{module}: {} differs in content", path.display()));
}
}
}
}
assert!(
problems.is_empty(),
"the vendored C++ headers have drifted from the pinned submodule \
({} difference(s)).\n\n{}\n\n\
build.rs compiles the VENDORED copy ({}), so this is what users get; \
the submodule ({}) only records the upstream release. Refresh the \
vendored copy from the submodule, or revert the local edit.",
problems.len(),
problems.join("\n"),
vendored_root.display(),
submodule_root.display(),
);
}