Creates a library to be used in each extension's build.rs that includes a BuildInfo struct with information from cargo, rustc and pijul.
IAMJPC2QCHQGUON43V7JL5K5S23TKQORKTQBUGWHCI6EEK2TPDQQC use std::num::NonZeroUsize;use camino::{Utf8Path, Utf8PathBuf};use libpijul::pristine::sanakirja::Pristine;use libpijul::{Base32, RecordBuilder, TxnT, TxnTExt};use quote::quote;#[derive(Debug)]pub struct Build {pub package: Package,pub toolchain: Toolchain,pub compilation: Compilation,pub repository: Repository,pub timestamp: jiff::Timestamp,}#[derive(Debug)]pub struct Package {pub package_name: &'static str,pub package_version: &'static str,}impl Package {fn export() -> Result<proc_macro2::TokenStream, anyhow::Error> {let package_name = std::env::var("CARGO_PKG_NAME")?;let package_version = &std::env::var("CARGO_PKG_VERSION")?;Ok(quote! {extension_build_info::Package {package_name: #package_name,package_version: #package_version,}})}}#[derive(Debug)]pub struct Toolchain {pub cargo_version: &'static str,pub rustc_version: &'static str,}impl Toolchain {fn get_version(program_location: String) -> Result<String, anyhow::Error> {let version_output = std::process::Command::new(program_location).arg("--version").output()?;let version_string = String::from_utf8(version_output.stdout)?;Ok(match version_string.strip_suffix('\n') {Some(version) => version.to_string(),None => version_string,})}fn export() -> Result<proc_macro2::TokenStream, anyhow::Error> {let cargo_version = Self::get_version(std::env::var("CARGO")?)?;let rustc_version = Self::get_version(std::env::var("RUSTC")?)?;Ok(quote! {extension_build_info::Toolchain {cargo_version: #cargo_version,rustc_version: #rustc_version,}})}}#[derive(Debug)]pub struct Compilation {pub target: &'static str,pub profile: &'static str,pub opt_level: &'static str,pub debug: &'static str,pub rustflags: &'static [&'static str],}impl Compilation {fn export() -> Result<proc_macro2::TokenStream, anyhow::Error> {let target = std::env::var("TARGET")?;let profile = std::env::var("PROFILE")?;let opt_level = std::env::var("OPT_LEVEL")?;let debug = std::env::var("DEBUG")?;let encoded_rustflags = std::env::var("CARGO_ENCODED_RUSTFLAGS")?;let rustflags = encoded_rustflags.split('\u{1f}');Ok(quote! {extension_build_info::Compilation {target: #target,profile: #profile,opt_level: #opt_level,debug: #debug,rustflags: &[#(#rustflags),*],}})}}#[derive(Debug)]pub struct Repository {pub channel_name: &'static str,pub channel_state: &'static str,pub has_unrecorded_changes: bool,}impl Repository {fn export() -> Result<proc_macro2::TokenStream, anyhow::Error> {let extension_manifest_directory = Utf8PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);let repository_root = extension_manifest_directory.parent().unwrap().parent().unwrap();let pristine_db = repository_root.join(libpijul::DOT_DIR).join("pristine").join("db");let pristine = Pristine::new(pristine_db.as_str())?;let change_store =libpijul::changestore::filesystem::FileSystem::from_root(repository_root.as_str(), 256);let working_copy = libpijul::working_copy::FileSystem::from_root(repository_root.as_str());// Recorded changeslet transaction = pristine.arc_txn_begin()?;let read_transaction = transaction.read();let channel_name = read_transaction.current_channel().unwrap_or(libpijul::DEFAULT_CHANNEL);let channel = read_transaction.load_channel(channel_name)?.unwrap();let channel_merkle = read_transaction.current_state(&*channel.read())?;let channel_state = channel_merkle.to_base32();// Unrecorded changeslet mut record_builder = RecordBuilder::new();record_builder.record(transaction.clone(),libpijul::Algorithm::default(),true,&libpijul::DEFAULT_SEPARATOR,channel.clone(),&working_copy,&change_store,"",std::thread::available_parallelism().unwrap_or(NonZeroUsize::MIN).get(),)?;let unrecorded_changes = record_builder.finish();let has_unrecorded_changes = !unrecorded_changes.actions.is_empty();Ok(quote! {extension_build_info::Repository {channel_name: #channel_name,channel_state: #channel_state,has_unrecorded_changes: #has_unrecorded_changes,}})}}#[macro_export]macro_rules! include_build_info {() => {include!(concat!(env!("OUT_DIR"), "/build_info.rs"))};}pub fn export() -> Result<(), anyhow::Error> {let timestamp = jiff::Timestamp::now();let timestamp_seconds = timestamp.as_second();let timestamp_nanoseconds = timestamp.subsec_nanosecond();let package = Package::export()?;let toolchain = Toolchain::export()?;let compilation = Compilation::export()?;let repository = Repository::export()?;let token_stream = quote! {extension_build_info::Build {package: #package,toolchain: #toolchain,compilation: #compilation,repository: #repository,timestamp: jiff::Timestamp::constant(#timestamp_seconds, #timestamp_nanoseconds),}};let out_dir = std::env::var("OUT_DIR")?;let file_path = Utf8Path::new(&out_dir).join("build_info.rs");std::fs::write(file_path, token_stream.to_string().as_bytes())?;Ok(())}
[package]name = "extension-build-info"publish = falseversion = "0.1.0"edition = "2024"[dependencies]anyhow.workspace = truecamino.workspace = truejiff.workspace = truelibpijul.workspace = trueproc-macro2.workspace = truequote.workspace = true