use flate2::{Compression, GzBuilder};
use std::fmt::Write as _;
use std::io::Write;
use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder, RustcBuilder};
struct EmbeddedTextAsset {
source: &'static str,
output_name: &'static str,
metadata_prefix: &'static str,
checksum_prefix: Option<&'static str>,
}
const EMBEDDED_TEXT_ASSETS: [EmbeddedTextAsset; 4] = [
EmbeddedTextAsset {
source: "legacy_pi_mono_code/pi-mono/packages/ai/src/models.generated.ts",
output_name: "legacy-models-generated.ts.gz",
metadata_prefix: "LEGACY_MODELS_GENERATED_TS",
checksum_prefix: Some("LEGACY_MODELS_GENERATED_TS"),
},
EmbeddedTextAsset {
source: "docs/provider-upstream-model-ids-snapshot.json",
output_name: "provider-upstream-model-ids.json.gz",
metadata_prefix: "PROVIDER_UPSTREAM_MODEL_IDS_JSON",
checksum_prefix: Some("PROVIDER_UPSTREAM_MODEL_IDS_JSON"),
},
EmbeddedTextAsset {
source: "docs/extension-artifact-provenance.json",
output_name: "extension-artifact-provenance.json.gz",
metadata_prefix: "EXTENSION_ARTIFACT_PROVENANCE_JSON",
checksum_prefix: None,
},
EmbeddedTextAsset {
source: "CHANGELOG.md",
output_name: "changelog.md.gz",
metadata_prefix: "CHANGELOG",
checksum_prefix: None,
},
];
fn grouped_decimal(value: impl std::fmt::Display) -> String {
let digits = value.to_string();
let mut grouped = String::with_capacity(digits.len() + digits.len() / 3);
for (index, digit) in digits.chars().enumerate() {
if index > 0 && (digits.len() - index).is_multiple_of(3) {
grouped.push('_');
}
grouped.push(digit);
}
grouped
}
fn compress_embedded_text_assets() -> Result<(), Box<dyn std::error::Error>> {
let out_dir = std::path::PathBuf::from(
std::env::var_os("OUT_DIR")
.ok_or_else(|| std::io::Error::other("OUT_DIR is unset for build script"))?,
);
let mut metadata = String::from("// @generated by build.rs; do not edit.\n");
for asset in EMBEDDED_TEXT_ASSETS {
let source = asset.source;
println!("cargo:rerun-if-changed={source}");
let source_bytes = std::fs::read(source)?;
std::str::from_utf8(&source_bytes).map_err(|error| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("embedded text resource {source} is not valid UTF-8: {error}"),
)
})?;
let mut encoder = GzBuilder::new()
.mtime(0)
.operating_system(255)
.write(Vec::new(), Compression::best());
encoder.write_all(&source_bytes)?;
std::fs::write(out_dir.join(asset.output_name), encoder.finish()?)?;
writeln!(
metadata,
"const {}_RAW_LEN: usize = {};",
asset.metadata_prefix,
grouped_decimal(source_bytes.len())
)?;
if let Some(prefix) = asset.checksum_prefix {
writeln!(
metadata,
"const {prefix}_CRC32C: u32 = {};",
grouped_decimal(crc32c::crc32c(&source_bytes))
)?;
}
}
std::fs::write(out_dir.join("embedded-text-metadata.rs"), metadata)?;
Ok(())
}
fn emit_benchmark_build_fingerprint() -> Result<(), Box<dyn std::error::Error>> {
fn required_env(name: &str) -> Result<String, std::io::Error> {
std::env::var(name).map_err(|_| {
std::io::Error::other(format!(
"Cargo did not provide required build variable {name}"
))
})
}
let profile_family = required_env("PROFILE")?;
let opt_level = required_env("OPT_LEVEL")?;
let debug = required_env("DEBUG")?;
let mut features = std::env::vars()
.filter_map(|(name, _)| {
name.strip_prefix("CARGO_FEATURE_")
.map(|feature| feature.to_ascii_lowercase().replace('_', "-"))
})
.collect::<Vec<_>>();
features.sort_unstable();
features.dedup();
println!("cargo:rustc-env=PI_BUILD_PROFILE_FAMILY={profile_family}");
println!("cargo:rustc-env=PI_BUILD_OPT_LEVEL={opt_level}");
println!("cargo:rustc-env=PI_BUILD_DEBUG={debug}");
println!("cargo:rustc-env=PI_BUILD_FEATURES={}", features.join(","));
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
compress_embedded_text_assets()?;
emit_benchmark_build_fingerprint()?;
let build = BuildBuilder::default().build_timestamp(true).build()?;
let cargo = CargoBuilder::default().target_triple(true).build()?;
let gix = GixBuilder::default().sha(true).dirty(true).build()?;
let rustc = RustcBuilder::default().semver(true).build()?;
let mut emitter = Emitter::default();
emitter
.quiet()
.add_instructions(&build)?
.add_instructions(&cargo)?
.add_instructions(&gix)?
.add_instructions(&rustc)?
.emit()?;
Ok(())
}