use std::env;
use std::fs;
use std::path::Path;
const SETS: &[&str] = &["sntrup653", "sntrup761", "sntrup857", "sntrup953", "sntrup1013", "sntrup1277"];
fn cargo_feature_enabled(name: &str) -> bool {
env::var_os(format!("CARGO_FEATURE_{}", name.to_uppercase())).is_some()
}
fn parse_define(line: &str) -> (String, String) {
let body = line.strip_prefix("-D").unwrap_or(line);
let (name, value) = body
.split_once('=')
.unwrap_or_else(|| panic!("expected NAME=VALUE in define line: {line}"));
(name.to_string(), value.to_string())
}
fn compile_dir(
dir: &Path,
includes: &[&Path],
namespace_headers: &[&Path],
defines: &[(String, String)],
is_msvc: bool,
out_name: &str,
) {
let mut build = cc::Build::new();
build.std("c11");
build.warnings(false);
for inc in includes {
build.include(inc);
}
for ns in namespace_headers {
build.flag(if is_msvc { "/FI" } else { "-include" });
build.flag(ns.to_str().unwrap());
}
for (name, value) in defines {
build.define(name, Some(value.as_str()));
}
let mut file_count = 0;
for entry in fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
if file_name.starts_with("._") {
continue;
}
if path.extension().and_then(|e| e.to_str()) == Some("c") {
build.file(&path);
file_count += 1;
}
println!("cargo:rerun-if-changed={}", path.display());
}
assert!(file_count > 0, "no .c files found in {}", dir.display());
build.compile(out_name);
}
fn main() {
let vendor = Path::new(env!("CARGO_MANIFEST_DIR")).join("vendor");
let common_dir = vendor.join("common");
let algo_dir = vendor.join("algo");
let common_ns = common_dir.join("namespace.h");
let is_msvc = env::var("TARGET").map(|t| t.contains("msvc")).unwrap_or(false);
if SETS.iter().any(|s| cargo_feature_enabled(s)) {
compile_dir(&common_dir, &[&common_dir], &[&common_ns], &[], is_msvc, "sntrup_common");
}
for &set in SETS {
if !cargo_feature_enabled(set) {
continue;
}
let set_dir = vendor.join("sets").join(set);
let flags_path = set_dir.join("BUILD_FLAGS.txt");
let defines: Vec<(String, String)> = fs::read_to_string(&flags_path)
.unwrap_or_else(|e| panic!("reading {}: {}", flags_path.display(), e))
.lines()
.map(str::trim)
.filter(|l| !l.is_empty())
.map(parse_define)
.collect();
let set_ns = set_dir.join("namespace.h");
compile_dir(
&algo_dir,
&[&set_dir, &algo_dir, &common_dir],
&[&common_ns, &set_ns],
&defines,
is_msvc,
&format!("{}_ref", set),
);
}
}