From 0106dd73bfa19b2c73aa436e8bab3240f237aaf3 Mon Sep 17 00:00:00 2001 From: Mark Dittmer Date: Thu, 14 May 2026 13:54:46 +0000 Subject: [PATCH] Refactor Config layout and rework platform binding parameters gherrit-pr-id: Geqdyjyofviodo455gi2nx3blswritlty --- anneal/v2/toolchain-config/build.rs | 13 -- anneal/v2/toolchain-config/src/lib.rs | 285 +++++++++----------------- 2 files changed, 94 insertions(+), 204 deletions(-) delete mode 100644 anneal/v2/toolchain-config/build.rs diff --git a/anneal/v2/toolchain-config/build.rs b/anneal/v2/toolchain-config/build.rs deleted file mode 100644 index 94213a69fb..0000000000 --- a/anneal/v2/toolchain-config/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=Cargo.toml"); - - println!( - "cargo:rustc-env=SETUP_ARCHIVE_BASE_URL=https://github.com/google/zerocopy/releases/download/build-2026.04.19.084341-62d9816418bdb3d566381c1a4070784f7cf5380e", - ); - for arch in ["LINUX_AARCH64", "LINUX_X86_64", "MACOS_AARCH64", "MACOS_X86_64"] { - println!( - "cargo:rustc-env=SETUP_ARCHIVE_SHA256_{arch}=54c9eabc88dbb604758edce1b51287ccd69c8a93db003278ea9ad4e97bb24a05", - ); - } -} diff --git a/anneal/v2/toolchain-config/src/lib.rs b/anneal/v2/toolchain-config/src/lib.rs index f5c9931554..0509226825 100644 --- a/anneal/v2/toolchain-config/src/lib.rs +++ b/anneal/v2/toolchain-config/src/lib.rs @@ -1,139 +1,47 @@ -/// Decodes a hexadecimal string into its byte representation. -const fn decode_hex(s: &str) -> Option<[u8; 32]> { - let bytes = s.as_bytes(); - if bytes.len() != 64 { - return None; - } - let mut res = [0u8; 32]; - let mut i = 0; - while i < 32 { - let (h, l) = (bytes[i * 2], bytes[i * 2 + 1]); - let h_nib = match decode_nibble(h) { - Some(n) => n, - None => return None, - }; - let l_nib = match decode_nibble(l) { - Some(n) => n, - None => return None, - }; - res[i] = (h_nib << 4) | l_nib; - i += 1; - } - Some(res) +/// Setup configuration binding external platform environments and baseline paths. +#[derive(Debug, Clone)] +pub struct Config<'a> { + pub os: &'a str, + pub arch: &'a str, + pub url: &'a str, + pub sha256: [u8; 32], } -const fn decode_nibble(c: u8) -> Option { - match c { - b'0'..=b'9' => Some(c - b'0'), - b'a'..=b'f' => Some(c - b'a' + 10), - b'A'..=b'F' => Some(c - b'A' + 10), - _ => None, - } +/// Optional runtime override directing installation from local dev builds instead of remote hosts. +#[derive(Debug, Clone)] +pub enum LocalOverride { + Dir(std::path::PathBuf), + Archive(std::path::PathBuf), } -macro_rules! decode_hex_env { - ($key:expr) => { - const { decode_hex(env!($key)).expect("valid hex") } - }; -} - -/// Supported platforms for Anneal dependencies. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Platform { - LinuxX86_64, - LinuxAArch64, - MacosAArch64, - MacosX86_64, -} - -impl Platform { - /// Returns the target triple for this platform. - pub fn triple(&self) -> &'static str { - match self { - Self::LinuxX86_64 => "x86_64-unknown-linux-gnu", - Self::LinuxAArch64 => "aarch64-unknown-linux-gnu", - Self::MacosAArch64 => "aarch64-apple-darwin", - Self::MacosX86_64 => "x86_64-apple-darwin", - } - } - - /// Detects the current host platform. - pub fn detect() -> Result { - let os = std::env::consts::OS; - let arch = std::env::consts::ARCH; - - match (os, arch) { - ("linux", "x86_64") => Ok(Self::LinuxX86_64), - ("linux", "aarch64") => Ok(Self::LinuxAArch64), - ("macos", "aarch64") => Ok(Self::MacosAArch64), - ("macos", "x86_64") => Ok(Self::MacosX86_64), - _ => Err(format!("Unsupported platform: {}-{}", os, arch)), +impl<'a> Config<'a> { + /// Instantiates static toolchain parameters auto-detecting current runtime OS and Architecture. + pub fn new(url: &'a str, sha256: [u8; 32]) -> Self { + Self { + os: std::env::consts::OS, + arch: std::env::consts::ARCH, + url, + sha256, } } - /// Returns the expected SHA-256 checksum for the specified archive on this - /// platform. - /// - /// These hashes are baked into the binary at compile time from values - /// provided by `build.rs` from the project's `Cargo.toml`. This ensures - /// that we always download and verify the exact versions of dependencies - /// that this version of Anneal was built to work with. - pub fn expected_archive_hash(&self) -> [u8; 32] { - use Platform::*; - match self { - LinuxX86_64 => decode_hex_env!("SETUP_ARCHIVE_SHA256_LINUX_X86_64"), - LinuxAArch64 => decode_hex_env!("SETUP_ARCHIVE_SHA256_LINUX_AARCH64"), - MacosAArch64 => decode_hex_env!("SETUP_ARCHIVE_SHA256_MACOS_AARCH64"), - MacosX86_64 => decode_hex_env!("SETUP_ARCHIVE_SHA256_MACOS_X86_64"), + /// Explicitly overrides target platform parameters for specialized configurations. + pub fn new_platform(os: &'a str, arch: &'a str, url: &'a str, sha256: [u8; 32]) -> Self { + Self { + os, + arch, + url, + sha256, } } - /// Returns the baseline remote URL from which the pre-verified toolchain archive - /// for this platform can be downloaded. - /// - /// This function constructs an absolute URL by interpolating the target platform's - /// specific archive filename onto a base URL baked into the binary at compile time - /// via `SETUP_ARCHIVE_BASE_URL`. This compile-time binding guarantees that downloads - /// exactly correspond to the release artifacts verified during the build phase. - pub fn remote_url(&self) -> String { - use Platform::*; - const BASE: &str = env!("SETUP_ARCHIVE_BASE_URL"); - match self { - LinuxX86_64 => format!("{BASE}/anneal-toolchain-linux-x86_64.tar.zst"), - LinuxAArch64 => format!("{BASE}/anneal-toolchain-linux-aarch64.tar.zst"), - MacosAArch64 => format!("{BASE}/anneal-toolchain-macos-aarch64.tar.zst"), - MacosX86_64 => format!("{BASE}/anneal-toolchain-macos-x64_64.tar.zst"), - } + /// Resolves the deterministic subdirectory path containing the verified toolchain files. + pub fn toolchain_dir(&self, root: &std::path::Path) -> std::path::PathBuf { + let expected_hex = encode_hex(&self.sha256); + root.join(&expected_hex[..6]) } } -/// Setup configuration -pub struct Config {} - -/// An archive format that can be extracted using a particular [`Extractor`] implementation. -pub enum ArchiveFormat { - TarZst, -} - -impl ArchiveFormat { - fn new_extractor(&self) -> impl Extractor { - use ArchiveFormat::*; - match self { - TarZst => TarZstLibraryExtractor, - } - } -} - -/// The source for an archive of dependencies that `setup` must put in place. -pub enum Source { - /// A fully assembled local directory tree containing uncompressed toolchain components. - LocalDirectory(std::path::PathBuf), - /// A local archive awaiting extraction. - LocalArchive(std::path::PathBuf, ArchiveFormat), - /// The archive format to expect from the statically configured remote URL source. - Remote(ArchiveFormat), -} - /// An abstract extraction factory instantiating operational output streams targeting designated /// filesystem paths. /// @@ -289,50 +197,48 @@ fn setup_from_directory(src: &std::path::Path, dst: &std::path::Path) -> Result< } fn setup_inner( - src: Source, + config: &Config<'_>, + local_override: Option, toolchain_dir: std::path::PathBuf, - expected_hash: [u8; 32], fetcher: impl FnOnce(&str) -> Result, String>, ) -> Result<(), String> { - let expected_hex = encode_hex(&expected_hash); - let subdir_name = &expected_hex[..6]; - let target_dir = toolchain_dir.join(subdir_name); - - use Source::*; - match src { - LocalArchive(path, format) => { - log::warn!( - "Toolchain contents from local archive may not match expected toolchain hash/version number." - ); - let extractor = format.new_extractor(); - let file = std::fs::File::open(path) - .map_err(|e| format!("Failed to open local archive: {e}"))?; - setup_from_archive(file, &target_dir, &extractor) - .map_err(|e| format!("Failed to extract archive: {e}"))?; - } - LocalDirectory(path) => { - log::warn!( - "Toolchain contents from local directory may not match expected toolchain hash/version number." - ); - setup_from_directory(&path, &target_dir)?; - } - Remote(format) => { - let extractor = format.new_extractor(); - let platform = Platform::detect().map_err(|e| format!("Failed to detect platform: {e}"))?; - let response = fetcher(&platform.remote_url())?; - - let actual_hash: [u8; 32] = setup_from_archive(response, &target_dir, &extractor) - .map_err(|e| format!("Failed to extract downloaded archive: {e}"))?; - - if actual_hash != expected_hash { - let _ = std::fs::remove_dir_all(&target_dir); - return Err(format!( - "Checksum mismatch for downloaded archive. Expected {}, got {}", - expected_hex, - encode_hex(&actual_hash) - )); + let target_dir = config.toolchain_dir(&toolchain_dir); + let expected_hex = encode_hex(&config.sha256); + + if let Some(override_src) = local_override { + match override_src { + LocalOverride::Archive(path) => { + log::warn!( + "Toolchain contents from local archive may not match expected toolchain hash/version number." + ); + let extractor = TarZstLibraryExtractor; + let file = std::fs::File::open(path) + .map_err(|e| format!("Failed to open local archive: {e}"))?; + setup_from_archive(file, &target_dir, &extractor) + .map_err(|e| format!("Failed to extract archive: {e}"))?; + } + LocalOverride::Dir(path) => { + log::warn!( + "Toolchain contents from local directory may not match expected toolchain hash/version number." + ); + setup_from_directory(&path, &target_dir)?; } } + } else { + let extractor = TarZstLibraryExtractor; + let response = fetcher(config.url)?; + + let actual_hash: [u8; 32] = setup_from_archive(response, &target_dir, &extractor) + .map_err(|e| format!("Failed to extract downloaded archive: {e}"))?; + + if actual_hash != config.sha256 { + let _ = std::fs::remove_dir_all(&target_dir); + return Err(format!( + "Checksum mismatch for downloaded archive. Expected {}, got {}", + expected_hex, + encode_hex(&actual_hash) + )); + } } Ok(()) @@ -342,17 +248,12 @@ fn setup_inner( /// /// This function processes the incoming dependency source and installs it into a toolchain /// directory named according to the source SHA256 hash. -/// -/// # Caveats -/// -/// - When `src` is a `Source::Local*` variant, the toolchain will be installed in a directory -/// named after the _expected_ SHA256 hash associated with the detected platform. This will not -/// necessarily match the behaviour of the platform's archive and should only be used for local -/// development of the managed toolchain itself. -pub fn setup(src: Source, toolchain_dir: std::path::PathBuf) -> Result<(), String> { - let platform = Platform::detect().expect("detect platform"); - let expected_hash = platform.expected_archive_hash(); - setup_inner(src, toolchain_dir, expected_hash, |url| { +pub fn setup( + config: &Config<'_>, + local_override: Option, + toolchain_dir: std::path::PathBuf, +) -> Result<(), String> { + setup_inner(config, local_override, toolchain_dir, |url| { let response = reqwest::blocking::get(url) .map_err(|e| format!("Failed to download archive: {e}"))?; let response = response @@ -431,13 +332,13 @@ mod tests { std::fs::write(src.join("test.txt"), "local_dir").unwrap(); let expected_hash = [1u8; 32]; - let expected_hex = encode_hex(&expected_hash); - let target_dir = temp.path().join(&expected_hex[..6]); + let config = Config::new("http://example.com", expected_hash); + let target_dir = config.toolchain_dir(temp.path()); setup_inner( - Source::LocalDirectory(src), + &config, + Some(LocalOverride::Dir(src)), temp.path().to_path_buf(), - expected_hash, |_| unreachable!(), ) .unwrap(); @@ -456,13 +357,13 @@ mod tests { create_test_archive(&src, &archive_path); let expected_hash = [2u8; 32]; - let expected_hex = encode_hex(&expected_hash); - let target_dir = temp.path().join(&expected_hex[..6]); + let config = Config::new("http://example.com", expected_hash); + let target_dir = config.toolchain_dir(temp.path()); setup_inner( - Source::LocalArchive(archive_path, ArchiveFormat::TarZst), + &config, + Some(LocalOverride::Archive(archive_path)), temp.path().to_path_buf(), - expected_hash, |_| unreachable!(), ) .unwrap(); @@ -481,15 +382,16 @@ mod tests { create_test_archive(&src, &archive_path); let actual_hash = compute_sha256(&archive_path); - let expected_hex = encode_hex(&actual_hash); - let target_dir = temp.path().join(&expected_hex[..6]); + let config = Config::new("http://example.com", actual_hash); + let target_dir = config.toolchain_dir(temp.path()); + let archive_path_clone = archive_path.clone(); setup_inner( - Source::Remote(ArchiveFormat::TarZst), + &config, + None, temp.path().to_path_buf(), - actual_hash, move |_url| { - let file = std::fs::File::open(&archive_path).unwrap(); + let file = std::fs::File::open(&archive_path_clone).unwrap(); Ok(Box::new(file)) }, ) @@ -512,15 +414,16 @@ mod tests { let mut expected_hash = actual_hash; expected_hash[0] ^= 1; // invalidate checksum - let expected_hex = encode_hex(&expected_hash); - let target_dir = temp.path().join(&expected_hex[..6]); + let config = Config::new("http://example.com", expected_hash); + let target_dir = config.toolchain_dir(temp.path()); + let archive_path_clone = archive_path.clone(); let res = setup_inner( - Source::Remote(ArchiveFormat::TarZst), + &config, + None, temp.path().to_path_buf(), - expected_hash, move |_url| { - let file = std::fs::File::open(&archive_path).unwrap(); + let file = std::fs::File::open(&archive_path_clone).unwrap(); Ok(Box::new(file)) }, );