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.ancestors().nth(2).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());
let 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();
let 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(())
}