From c1194dfb87030e4af3717e7b671d71e6b498e986 Mon Sep 17 00:00:00 2001 From: zackees Date: Tue, 21 Jul 2026 18:51:48 -0700 Subject: [PATCH] fix(toolchain): resolve RISC-V C++ multilib from GCC --- .../fbuild-toolchain/src/toolchain/riscv.rs | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/crates/fbuild-toolchain/src/toolchain/riscv.rs b/crates/fbuild-toolchain/src/toolchain/riscv.rs index 9870a75c5..497d7cb6b 100644 --- a/crates/fbuild-toolchain/src/toolchain/riscv.rs +++ b/crates/fbuild-toolchain/src/toolchain/riscv.rs @@ -6,6 +6,7 @@ //! riscv-none-elf-size. use std::path::{Path, PathBuf}; +use std::process::Command; use crate::{CacheSubdir, PackageBase, PackageInfo, Toolchain}; @@ -78,9 +79,11 @@ impl RiscvToolchain { let root = self.resolved_dir(); let mut dirs = Vec::new(); - // GCC multilib directories use only the base ISA (rv32ec), not sub-extensions - // like _zicsr. Strip everything from the first underscore. - let multilib_march = march.split('_').next().unwrap_or(march); + // Ask GCC for the selected multilib instead of deriving its directory from + // the ISA spelling. The default multilib is reported as `.`. + let multilib_dir = self + .get_gcc_multilib_dir(march, mabi) + .unwrap_or_else(|| PathBuf::from(march.split('_').next().unwrap_or(march)).join(mabi)); // C++ headers: find the version directory dynamically let cxx_base = root.join("riscv-none-elf").join("include").join("c++"); @@ -91,10 +94,7 @@ impl RiscvToolchain { // 1. Base C++ headers dirs.push(version_dir.clone()); // 2. Multilib-specific - let multilib = version_dir - .join("riscv-none-elf") - .join(multilib_march) - .join(mabi); + let multilib = multilib_include_path(&version_dir, &multilib_dir); if multilib.is_dir() { dirs.push(multilib); } @@ -138,6 +138,26 @@ impl RiscvToolchain { dirs } + fn get_gcc_multilib_dir(&self, march: &str, mabi: &str) -> Option { + let output = Command::new(self.get_gcc_path()) + .args([ + format!("-march={march}"), + format!("-mabi={mabi}"), + "-print-multi-directory".into(), + ]) + .output() + .ok()?; + if !output.status.success() { + return None; + } + let directory = String::from_utf8(output.stdout).ok()?.trim().to_owned(); + if directory.is_empty() || directory == "." { + Some(PathBuf::new()) + } else { + Some(PathBuf::from(directory)) + } + } + /// Validate that the toolchain installation has all required files. fn validate(install_dir: &Path) -> fbuild_core::Result<()> { let root = find_bin_root(install_dir); @@ -172,6 +192,10 @@ impl RiscvToolchain { } } +fn multilib_include_path(version_dir: &Path, multilib_dir: &Path) -> PathBuf { + version_dir.join("riscv-none-elf").join(multilib_dir) +} + #[async_trait::async_trait] impl crate::Package for RiscvToolchain { async fn ensure_installed(&self) -> fbuild_core::Result { @@ -388,6 +412,24 @@ mod tests { assert!(!tc.is_installed()); } + #[test] + fn test_multilib_include_path_maps_default_directory_to_sysroot() { + let version_dir = Path::new("toolchain/include/c++/14.2.0"); + assert_eq!( + multilib_include_path(version_dir, Path::new("")), + PathBuf::from("toolchain/include/c++/14.2.0/riscv-none-elf") + ); + } + + #[test] + fn test_multilib_include_path_preserves_extension_directory() { + let version_dir = Path::new("toolchain/include/c++/14.2.0"); + assert_eq!( + multilib_include_path(version_dir, Path::new("rv32imafc_zicsr/ilp32f")), + PathBuf::from("toolchain/include/c++/14.2.0/riscv-none-elf/rv32imafc_zicsr/ilp32f") + ); + } + /// Every platform entry must have a valid URL. #[test] fn test_all_platform_urls_are_valid() {