use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=proto/daemon.proto");
println!("cargo:rerun-if-changed=proto/broker_v1/broker_v1_envelope.proto");
println!("cargo:rerun-if-changed=proto/broker_v1/broker_v1_admin.proto");
println!("cargo:rerun-if-changed=proto/broker_v1/broker_v1_manifest.proto");
println!("cargo:rerun-if-changed=proto/broker_v1/broker_v1_service_def.proto");
println!("cargo:rerun-if-changed=proto/broker_v2/broker_v2_service_def.proto");
println!("cargo:rerun-if-changed=proto/broker_v2/broker_v2_control.proto");
println!("cargo:rerun-if-changed=build.rs");
let file_descriptors = protox::compile(
[
"proto/daemon.proto",
"proto/broker_v1/broker_v1_envelope.proto",
"proto/broker_v1/broker_v1_admin.proto",
"proto/broker_v1/broker_v1_manifest.proto",
"proto/broker_v1/broker_v1_service_def.proto",
"proto/broker_v2/broker_v2_service_def.proto",
"proto/broker_v2/broker_v2_control.proto",
],
["proto/"],
)?;
prost_build::compile_fds(file_descriptors)?;
generate_sidecar_hash_table()?;
Ok(())
}
fn generate_sidecar_hash_table() -> Result<(), Box<dyn std::error::Error>> {
let manifest_path = locate_workspace_manifest();
println!("cargo:rerun-if-changed={}", manifest_path.to_string_lossy());
let parsed = read_manifest(&manifest_path);
let rendered = render_hash_table(&parsed);
let out_dir = std::env::var_os("OUT_DIR").expect("cargo always sets OUT_DIR for build.rs");
let dest = Path::new(&out_dir).join("conpty_sidecar_hashes.rs");
std::fs::write(&dest, rendered)?;
Ok(())
}
fn locate_workspace_manifest() -> std::path::PathBuf {
let manifest_dir = std::env::var_os("CARGO_MANIFEST_DIR")
.expect("cargo always sets CARGO_MANIFEST_DIR for build.rs");
Path::new(&manifest_dir)
.join("..")
.join("..")
.join("conpty-sidecar.sha256.toml")
}
struct ParsedManifest {
x64: Option<(String, u64)>,
arm64: Option<(String, u64)>,
x86: Option<(String, u64)>,
arm: Option<(String, u64)>,
}
fn read_manifest(path: &Path) -> ParsedManifest {
let mut parsed = ParsedManifest {
x64: None,
arm64: None,
x86: None,
arm: None,
};
let Ok(raw) = std::fs::read_to_string(path) else {
println!(
"cargo:warning=conpty-sidecar.sha256.toml not found at {}; \
runtime will skip SHA-256 verification on Win10 sidecar fetch",
path.display()
);
return parsed;
};
let table: toml::Value = match raw.parse() {
Ok(v) => v,
Err(e) => {
println!(
"cargo:warning=failed to parse conpty-sidecar.sha256.toml: {e}; \
runtime will skip SHA-256 verification"
);
return parsed;
}
};
let Some(assets) = table.get("asset").and_then(|v| v.as_table()) else {
return parsed;
};
for arch in ["x64", "arm64", "x86", "arm"] {
let Some(entry) = assets.get(arch).and_then(|v| v.as_table()) else {
continue;
};
let Some(sha) = entry.get("sha256").and_then(|v| v.as_str()) else {
continue;
};
let size = entry
.get("size_bytes")
.and_then(|v| v.as_integer())
.map(|n| n.max(0) as u64)
.unwrap_or(0);
let slot = match arch {
"x64" => &mut parsed.x64,
"arm64" => &mut parsed.arm64,
"x86" => &mut parsed.x86,
"arm" => &mut parsed.arm,
_ => unreachable!(),
};
*slot = Some((sha.to_owned(), size));
}
parsed
}
fn render_hash_table(parsed: &ParsedManifest) -> String {
let mut out = String::new();
out.push_str(
"// Generated by build.rs at compile time from \
`conpty-sidecar.sha256.toml`. Do not edit.\n\n\
pub(super) struct ExpectedAsset {\n \
pub sha256_hex: &'static str,\n \
pub size_bytes: u64,\n\
}\n\n",
);
for (name, value) in [
("EXPECTED_X64", &parsed.x64),
("EXPECTED_ARM64", &parsed.arm64),
("EXPECTED_X86", &parsed.x86),
("EXPECTED_ARM", &parsed.arm),
] {
let body = match value {
Some((sha, size)) => {
format!("Some(ExpectedAsset {{ sha256_hex: \"{sha}\", size_bytes: {size} }})",)
}
None => "None".to_owned(),
};
out.push_str(&format!(
"#[allow(dead_code)]\n\
pub(super) const {name}: Option<ExpectedAsset> = {body};\n",
));
}
out
}