2024-06-02 18:15:21 -04:00
|
|
|
use glob::glob;
|
2024-01-20 21:52:23 -05:00
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
use std::fs::OpenOptions;
|
|
|
|
use std::io::BufRead;
|
|
|
|
use std::io::Write;
|
2023-10-08 12:40:17 -04:00
|
|
|
use std::{
|
2024-06-02 18:15:21 -04:00
|
|
|
env, io,
|
2023-11-07 19:27:08 -05:00
|
|
|
path::Path,
|
2023-10-08 12:40:17 -04:00
|
|
|
process::{Command, Stdio},
|
|
|
|
};
|
2023-09-15 11:45:12 -04:00
|
|
|
|
2024-05-14 08:46:44 -04:00
|
|
|
const CAPNP_VERSION: &str = "1.0.2";
|
2023-09-16 12:23:56 -04:00
|
|
|
|
2023-09-15 11:45:12 -04:00
|
|
|
fn get_desired_capnp_version_string() -> String {
|
2023-09-19 19:12:51 -04:00
|
|
|
CAPNP_VERSION.to_string()
|
|
|
|
}
|
|
|
|
|
2023-09-15 11:45:12 -04:00
|
|
|
fn get_capnp_version_string() -> String {
|
2023-09-16 13:07:12 -04:00
|
|
|
let output = Command::new("capnp")
|
2023-09-15 11:45:12 -04:00
|
|
|
.arg("--version")
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
.output()
|
2023-11-07 20:38:36 -05:00
|
|
|
.expect("capnp was not in the PATH, and is required for the build when you have changed any .capnp files");
|
2023-09-15 11:45:12 -04:00
|
|
|
let s = String::from_utf8(output.stdout)
|
2023-09-16 13:07:12 -04:00
|
|
|
.expect("'capnp --version' output was not a valid string")
|
2023-09-15 11:45:12 -04:00
|
|
|
.trim()
|
|
|
|
.to_owned();
|
|
|
|
|
|
|
|
if !s.starts_with("Cap'n Proto version ") {
|
2023-09-16 13:07:12 -04:00
|
|
|
panic!("invalid capnp version string: {}", s);
|
2023-09-15 11:45:12 -04:00
|
|
|
}
|
|
|
|
s[20..].to_owned()
|
|
|
|
}
|
|
|
|
|
2024-01-20 21:52:23 -05:00
|
|
|
fn is_input_file_outdated<P, Q>(input: P, output: Q) -> io::Result<bool>
|
2023-11-07 19:27:08 -05:00
|
|
|
where
|
2024-01-20 21:52:23 -05:00
|
|
|
P: AsRef<Path>,
|
|
|
|
Q: AsRef<Path>,
|
2023-11-07 19:27:08 -05:00
|
|
|
{
|
2024-05-17 03:04:49 -04:00
|
|
|
let (Some(out_bh), Some(out_capnp_hash)) = get_build_hash_and_capnp_version_hash(output) else {
|
2024-01-20 21:52:23 -05:00
|
|
|
// output file not found or no build hash, we are outdated
|
2024-05-17 03:04:49 -04:00
|
|
|
println!("cargo:warning=Output file not found or no build hash.");
|
2024-01-20 21:52:23 -05:00
|
|
|
return Ok(true);
|
|
|
|
};
|
|
|
|
|
2024-05-18 11:16:21 -04:00
|
|
|
// Check if desired CAPNP_VERSION hash has changed
|
2024-05-17 03:04:49 -04:00
|
|
|
let mut hasher = Sha256::new();
|
2024-05-18 11:16:21 -04:00
|
|
|
hasher.update(get_desired_capnp_version_string().as_bytes());
|
2024-05-18 12:16:37 -04:00
|
|
|
let capnp_hash = hasher.finalize().to_vec();
|
2024-05-17 03:04:49 -04:00
|
|
|
|
2024-01-20 21:52:23 -05:00
|
|
|
let in_bh = make_build_hash(input);
|
|
|
|
|
2024-05-17 03:04:49 -04:00
|
|
|
if out_bh != in_bh {
|
|
|
|
println!("cargo:warning=Build hash has changed.");
|
|
|
|
return Ok(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if out_capnp_hash != capnp_hash {
|
2024-05-18 11:16:21 -04:00
|
|
|
println!("cargo:warning=Capnp desired version hash has changed.");
|
2024-05-17 03:04:49 -04:00
|
|
|
return Ok(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(false)
|
2024-01-20 21:52:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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 22:13:00 -04:00
|
|
|
}
|
2024-01-20 21:52:23 -05:00
|
|
|
let out = hasher.finalize();
|
|
|
|
out.to_vec()
|
|
|
|
}
|
|
|
|
|
2024-05-17 03:04:49 -04:00
|
|
|
fn get_build_hash_and_capnp_version_hash<Q: AsRef<Path>>(
|
|
|
|
output_path: Q,
|
|
|
|
) -> (Option<Vec<u8>>, Option<Vec<u8>>) {
|
2024-05-18 11:49:52 -04:00
|
|
|
let output_file = match std::fs::File::open(output_path).ok() {
|
|
|
|
// Returns a file handle if the file exists
|
|
|
|
Some(f) => f,
|
|
|
|
// Returns None, None if the file does not exist
|
|
|
|
None => return (None, None),
|
|
|
|
};
|
|
|
|
let lines = std::io::BufReader::new(output_file).lines();
|
2024-05-17 03:04:49 -04:00
|
|
|
let mut build_hash = None;
|
|
|
|
let mut capnp_version_hash = None;
|
2024-01-20 21:52:23 -05:00
|
|
|
for l in lines {
|
|
|
|
let l = l.unwrap();
|
2024-01-21 15:50:48 -05:00
|
|
|
if let Some(rest) = l.strip_prefix("//BUILDHASH:") {
|
2024-05-17 03:04:49 -04:00
|
|
|
build_hash = Some(hex::decode(rest).unwrap());
|
2024-05-18 11:16:21 -04:00
|
|
|
} else if let Some(rest) = l.strip_prefix("//CAPNPDESIREDVERSIONHASH:") {
|
2024-05-17 03:04:49 -04:00
|
|
|
capnp_version_hash = Some(hex::decode(rest).unwrap());
|
2024-01-20 21:52:23 -05:00
|
|
|
}
|
|
|
|
}
|
2024-05-17 03:04:49 -04:00
|
|
|
(build_hash, capnp_version_hash)
|
2024-01-20 21:52:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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-18 11:49:52 -04:00
|
|
|
fn append_hash_and_desired_capnp_version_hash<P: AsRef<Path>, Q: AsRef<Path>>(
|
2024-05-17 03:04:49 -04:00
|
|
|
input_path: P,
|
|
|
|
output_path: Q,
|
|
|
|
) {
|
2024-01-20 21:52:23 -05:00
|
|
|
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 03:04:49 -04:00
|
|
|
|
2024-03-03 00:42:22 -05:00
|
|
|
let mut out_file = OpenOptions::new().append(true).open(output_path).unwrap();
|
2024-01-20 21:52:23 -05:00
|
|
|
writeln!(out_file, "\n//BUILDHASH:{}", hex::encode(h)).unwrap();
|
2024-05-17 03:04:49 -04:00
|
|
|
|
|
|
|
let mut hasher = Sha256::new();
|
2024-05-18 11:51:55 -04:00
|
|
|
hasher.update(get_desired_capnp_version_string().as_bytes());
|
2024-05-17 03:04:49 -04:00
|
|
|
writeln!(
|
|
|
|
out_file,
|
2024-05-18 11:16:21 -04:00
|
|
|
"\n//CAPNPDESIREDVERSIONHASH:{}",
|
2024-05-17 03:04:49 -04:00
|
|
|
hex::encode(hasher.finalize())
|
|
|
|
)
|
|
|
|
.unwrap();
|
2023-11-07 19:27:08 -05:00
|
|
|
}
|
2023-09-15 11:45:12 -04:00
|
|
|
|
2023-11-07 19:27:08 -05:00
|
|
|
fn do_capnp_build() {
|
2023-10-17 22:13:00 -04:00
|
|
|
let desired_capnp_version_string = get_desired_capnp_version_string();
|
|
|
|
let capnp_version_string = get_capnp_version_string();
|
2023-09-15 11:45:12 -04:00
|
|
|
|
2023-10-17 22:13:00 -04: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 19:12:51 -04:00
|
|
|
|
2023-10-17 22:13:00 -04: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 19:12:51 -04:00
|
|
|
|
2023-10-17 22:13:00 -04:00
|
|
|
::capnpc::CompilerCommand::new()
|
|
|
|
.file("proto/veilid.capnp")
|
2023-11-07 14:19:28 -05:00
|
|
|
.output_path(".")
|
2023-10-17 22:13:00 -04:00
|
|
|
.run()
|
|
|
|
.expect("compiling schema");
|
2023-11-07 21:06:29 -05:00
|
|
|
|
2024-01-20 21:52:23 -05:00
|
|
|
// If successful, append a hash of the input to the output file
|
2024-05-18 11:49:52 -04:00
|
|
|
// Also append a hash of the desired capnp version to the output file
|
|
|
|
append_hash_and_desired_capnp_version_hash("proto/veilid.capnp", "proto/veilid_capnp.rs");
|
2023-11-07 19:27:08 -05:00
|
|
|
}
|
2023-10-17 22:13:00 -04:00
|
|
|
|
2024-06-02 18:15:21 -04:00
|
|
|
// Fix for missing __extenddftf2 on Android x86_64 Emulator
|
|
|
|
fn fix_android_emulator() {
|
|
|
|
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
|
|
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
|
|
|
if target_arch == "x86_64" && target_os == "android" {
|
|
|
|
let missing_library = "clang_rt.builtins-x86_64-android";
|
|
|
|
let android_home = env::var("ANDROID_HOME")
|
|
|
|
.or(env::var("ANDROID_SDK_ROOT"))
|
|
|
|
.expect("ANDROID_HOME or ANDROID_SDK_ROOT not set");
|
|
|
|
let lib_path = glob(&format!(
|
|
|
|
"{android_home}/ndk/26.3.11579264/**/lib{missing_library}.a"
|
|
|
|
))
|
|
|
|
.expect("failed to glob")
|
|
|
|
.next()
|
|
|
|
.expect("Need libclang_rt.builtins-x86_64-android.a")
|
|
|
|
.unwrap();
|
|
|
|
let lib_dir = lib_path.parent().unwrap();
|
|
|
|
println!("cargo:rustc-link-search={}", lib_dir.display());
|
|
|
|
println!("cargo:rustc-link-lib=static={missing_library}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-03 13:24:08 -05:00
|
|
|
#[cfg(feature = "geolocation")]
|
|
|
|
fn download_file(url: &str, filename: impl AsRef<Path>) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
let content = reqwest::blocking::get(url)?.bytes()?;
|
|
|
|
std::fs::write(filename, content)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "geolocation")]
|
|
|
|
fn download_geoip_database_files() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
let manifest_dir = env::var("CARGO_MANIFEST_DIR")?;
|
|
|
|
let target_dir = std::path::PathBuf::from(manifest_dir).join("../target");
|
|
|
|
|
|
|
|
// Source: https://github.com/sapics/ip-location-db
|
|
|
|
let files = [
|
|
|
|
(
|
|
|
|
"https://cdn.jsdelivr.net/npm/@ip-location-db/asn-country-mmdb/asn-country-ipv4.mmdb",
|
|
|
|
Path::new(&target_dir).join("ipv4.mmdb"),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"https://cdn.jsdelivr.net/npm/@ip-location-db/asn-country-mmdb/asn-country-ipv6.mmdb",
|
|
|
|
Path::new(&target_dir).join("ipv6.mmdb"),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
for (url, filename) in files {
|
|
|
|
if !filename.exists() {
|
|
|
|
println!("Downloading {url}");
|
|
|
|
download_file(url, &filename)?;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let modified = std::fs::metadata(&filename)?.modified()?;
|
|
|
|
let now = std::time::SystemTime::now();
|
|
|
|
let time_diff = now.duration_since(modified)?;
|
|
|
|
|
|
|
|
if time_diff > std::time::Duration::from_secs(60 * 60 * 24) {
|
|
|
|
println!("Downloading {url}");
|
|
|
|
download_file(url, &filename)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:27:08 -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 03:04:49 -04:00
|
|
|
if is_input_file_outdated("./proto/veilid.capnp", "./proto/veilid_capnp.rs").unwrap() {
|
2024-01-20 21:52:23 -05:00
|
|
|
println!("cargo:warning=rebuilding proto/veilid_capnp.rs because it has changed from the last generation of proto/veilid.capnp");
|
2023-11-07 19:27:08 -05:00
|
|
|
do_capnp_build();
|
|
|
|
}
|
2024-06-02 18:15:21 -04:00
|
|
|
|
|
|
|
fix_android_emulator();
|
2024-11-03 13:24:08 -05:00
|
|
|
|
|
|
|
#[cfg(feature = "geolocation")]
|
|
|
|
download_geoip_database_files().expect("failed to download geoip database files");
|
2023-11-07 19:27:08 -05:00
|
|
|
}
|