veilid/veilid-core/build.rs

177 lines
5.4 KiB
Rust
Raw Normal View History

use sha2::{Digest, Sha256};
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::Write;
use std::{
io,
path::Path,
process::{Command, Stdio},
};
2023-09-15 10:45:12 -05:00
2024-05-14 07:46:44 -05:00
const CAPNP_VERSION: &str = "1.0.2";
2023-09-16 11:23:56 -05:00
2023-09-15 10:45:12 -05:00
fn get_desired_capnp_version_string() -> String {
2023-09-19 18:12:51 -05:00
CAPNP_VERSION.to_string()
}
2023-09-15 10:45:12 -05:00
fn get_capnp_version_string() -> String {
2023-09-16 12:07:12 -05:00
let output = Command::new("capnp")
2023-09-15 10:45:12 -05:00
.arg("--version")
.stdout(Stdio::piped())
.output()
2023-11-07 19:38:36 -06:00
.expect("capnp was not in the PATH, and is required for the build when you have changed any .capnp files");
2023-09-15 10:45:12 -05:00
let s = String::from_utf8(output.stdout)
2023-09-16 12:07:12 -05:00
.expect("'capnp --version' output was not a valid string")
2023-09-15 10:45:12 -05:00
.trim()
.to_owned();
if !s.starts_with("Cap'n Proto version ") {
2023-09-16 12:07:12 -05:00
panic!("invalid capnp version string: {}", s);
2023-09-15 10:45:12 -05:00
}
s[20..].to_owned()
}
fn is_input_file_outdated<P, Q>(input: P, output: Q) -> io::Result<bool>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
2024-05-17 02:04:49 -05:00
let (Some(out_bh), Some(out_capnp_hash)) = get_build_hash_and_capnp_version_hash(output) else {
// output file not found or no build hash, we are outdated
2024-05-17 02:04:49 -05:00
println!("cargo:warning=Output file not found or no build hash.");
return Ok(true);
};
2024-05-17 02:04:49 -05:00
// Check if capnp version hash has changed
let mut hasher = Sha256::new();
hasher.update(get_capnp_version_string().as_bytes());
let capnp_hash = hex::encode(hasher.finalize()).as_bytes().to_vec();
let in_bh = make_build_hash(input);
2024-05-17 02:04:49 -05:00
if out_bh != in_bh {
println!("cargo:warning=Build hash has changed.");
return Ok(true);
}
if out_capnp_hash != capnp_hash {
println!("cargo:warning=Capnp version hash has changed.");
return Ok(true);
}
Ok(false)
}
fn calculate_hash(lines: std::io::Lines<std::io::BufReader<std::fs::File>>) -> Vec<u8> {
let mut hasher = Sha256::new();
// Build hash of lines, ignoring EOL conventions
for l in lines {
let l = l.unwrap();
hasher.update(l.as_bytes());
hasher.update(b"\n");
2023-10-17 21:13:00 -05:00
}
let out = hasher.finalize();
out.to_vec()
}
2024-05-17 02:04:49 -05:00
fn get_build_hash_and_capnp_version_hash<Q: AsRef<Path>>(
output_path: Q,
) -> (Option<Vec<u8>>, Option<Vec<u8>>) {
let lines = std::io::BufReader::new(std::fs::File::open(output_path).ok().unwrap()).lines();
let mut build_hash = None;
let mut capnp_version_hash = None;
for l in lines {
let l = l.unwrap();
2024-01-21 14:50:48 -06:00
if let Some(rest) = l.strip_prefix("//BUILDHASH:") {
2024-05-17 02:04:49 -05:00
build_hash = Some(hex::decode(rest).unwrap());
} else if let Some(rest) = l.strip_prefix("//CAPNPVERSIONHASH:") {
capnp_version_hash = Some(hex::decode(rest).unwrap());
}
}
2024-05-17 02:04:49 -05:00
(build_hash, capnp_version_hash)
}
fn make_build_hash<P: AsRef<Path>>(input_path: P) -> Vec<u8> {
let input_path = input_path.as_ref();
let lines = std::io::BufReader::new(std::fs::File::open(input_path).unwrap()).lines();
calculate_hash(lines)
}
2024-05-17 02:04:49 -05:00
fn append_hash_and_detected_capnp_version_hash<P: AsRef<Path>, Q: AsRef<Path>>(
input_path: P,
output_path: Q,
) {
let input_path = input_path.as_ref();
let output_path = output_path.as_ref();
let lines = std::io::BufReader::new(std::fs::File::open(input_path).unwrap()).lines();
let h = calculate_hash(lines);
2024-05-17 02:04:49 -05:00
2024-03-02 23:42:22 -06:00
let mut out_file = OpenOptions::new().append(true).open(output_path).unwrap();
writeln!(out_file, "\n//BUILDHASH:{}", hex::encode(h)).unwrap();
2024-05-17 02:04:49 -05:00
let mut hasher = Sha256::new();
hasher.update(get_capnp_version_string().as_bytes());
writeln!(
out_file,
"\n//CAPNPVERSIONHASH:{}",
hex::encode(hasher.finalize())
)
.unwrap();
}
2023-09-15 10:45:12 -05:00
fn do_capnp_build() {
2023-10-17 21:13:00 -05:00
let desired_capnp_version_string = get_desired_capnp_version_string();
let capnp_version_string = get_capnp_version_string();
2023-09-15 10:45:12 -05:00
2023-10-17 21:13:00 -05:00
// Check capnp version
let desired_capnp_major_version = desired_capnp_version_string
.split_once('.')
.unwrap()
.0
.parse::<usize>()
.expect("should be valid int");
2023-09-19 18:12:51 -05:00
2023-10-17 21:13:00 -05:00
if capnp_version_string
.split_once('.')
.unwrap()
.0
.parse::<usize>()
.expect("should be valid int")
!= desired_capnp_major_version
{
panic!(
"capnproto version should be major version 1, preferably {} but is {}",
desired_capnp_version_string, capnp_version_string
);
} else if capnp_version_string != desired_capnp_version_string {
println!(
"cargo:warning=capnproto version may be untested: {}",
capnp_version_string
);
}
2023-09-19 18:12:51 -05:00
2023-10-17 21:13:00 -05:00
::capnpc::CompilerCommand::new()
.file("proto/veilid.capnp")
2023-11-07 13:19:28 -06:00
.output_path(".")
2023-10-17 21:13:00 -05:00
.run()
.expect("compiling schema");
2023-11-07 20:06:29 -06:00
// If successful, append a hash of the input to the output file
2024-05-17 02:04:49 -05:00
// Also append a hash of the detected capnp version to the output file
append_hash_and_detected_capnp_version_hash("proto/veilid.capnp", "proto/veilid_capnp.rs");
}
2023-10-17 21:13:00 -05:00
fn main() {
if std::env::var("DOCS_RS").is_ok()
|| std::env::var("CARGO_CFG_DOC").is_ok()
|| std::env::var("BUILD_DOCS").is_ok()
{
return;
}
2024-05-17 02:04:49 -05:00
if is_input_file_outdated("./proto/veilid.capnp", "./proto/veilid_capnp.rs").unwrap() {
println!("cargo:warning=rebuilding proto/veilid_capnp.rs because it has changed from the last generation of proto/veilid.capnp");
do_capnp_build();
}
}