use crate::{DebugPath, util::execute_cmd};
use std::path::PathBuf;
pub fn get_toolchain_sysroot(debug_path: &DebugPath) -> Option<String> {
if debug_path.lang == Some("DW_LANG_Rust".into())
&& let Some(producer) = debug_path.producer.as_ref()
{
let (toolchain, platform_tools) =
rustc_toolchain_from_producer(producer).or_else(|| {
eprintln!("Failed to extract toolchain from DW_AT_producer");
None
})?;
let file_name = debug_path
.path
.file_name()
.map(|n| n.to_string_lossy())
.unwrap_or_default();
let pt = platform_tools
.as_ref()
.map(|v| format!(", platform-tools {v}"))
.unwrap_or_default();
eprintln!("{file_name} likely: compiler {producer}{pt}, toolchain {toolchain}");
let sysroot = execute_cmd(
&PathBuf::from("rustc"),
[&format!("+{toolchain}"), "--print", "sysroot"],
)
.or_else(|| {
eprintln!("Failed to extract sysroot for toolchain {toolchain}");
None
});
return sysroot.map(|s| format!("{}/lib/rustlib/src/rust", s.trim()));
}
None
}
pub fn rustc_toolchain_from_producer(producer: &str) -> Option<(String, Option<String>)> {
let after = producer.split("rustc version ").nth(1)?;
if !after.contains("-dev") {
let date = after
.split('(')
.nth(1)?
.split(')')
.next()?
.split_whitespace()
.nth(1)?;
Some((format!("nightly-{date}"), None))
} else {
let version_dev = after.split(')').next()?;
let rustc_version = version_dev.split('-').next()?;
let producer_rustc_commit_hash = version_dev
.split('(')
.nth(1)
.and_then(|inner| inner.split_whitespace().next())
.map(String::from);
let platform_tools_version =
get_platform_tools_version(rustc_version, producer_rustc_commit_hash.as_deref())?;
Some((
format!("{rustc_version}-sbpf-solana-{platform_tools_version}"),
Some(platform_tools_version),
))
}
}
pub fn cargo_home() -> String {
std::env::var("CARGO_HOME")
.unwrap_or_else(|_| format!("{}/.cargo", std::env::var("HOME").unwrap()))
}
pub fn get_platform_tools_version(
binary_rustc_version: &str,
producer_rustc_commit_hash: Option<&str>,
) -> Option<String> {
let home_dir = std::env::var("HOME").ok()?;
let base_line = format!("{}/.cache/solana", home_dir);
let paths = std::fs::read_dir(&base_line).ok()?;
let mut platform_tools_dirs = Vec::new();
for path in paths {
let Ok(path) = path else { continue };
let Ok(file_type) = path.file_type() else {
continue;
};
if !file_type.is_dir() {
continue;
}
let dir_name = path.file_name();
if !dir_name.to_string_lossy().starts_with("v") {
continue;
}
platform_tools_dirs.push(dir_name.to_string_lossy().to_string());
}
platform_tools_dirs.sort();
platform_tools_dirs
.iter()
.rev()
.map(|ver| {
(
ver.clone(),
format!("{}/{}/platform-tools/rust/bin/rustc", base_line, ver),
)
})
.filter(|(ver, rustc_path)| {
if !PathBuf::from(&rustc_path).is_file() {
return false;
}
let Some(platform_tools_rustc_version) =
execute_cmd(&PathBuf::from(rustc_path), ["--version"])
else {
return false;
};
let rustc_commit_hash =
std::fs::read_to_string(format!("{base_line}/{ver}/platform-tools/version.md"))
.ok()
.and_then(|version_file_content| {
version_file_content
.lines()
.find(|line| line.contains("rust.git"))
.and_then(|line| line.split(' ').next())
.map(String::from)
});
if let (Some(rch), Some(ch)) = (rustc_commit_hash, producer_rustc_commit_hash)
&& !rch.starts_with(ch)
{
return false;
}
platform_tools_rustc_version.contains(binary_rustc_version)
})
.map(|(ver, _)| ver)
.next()
}
pub fn map_dwarf_path(dwarf_path: &str, rust_src_root: Option<&str>, cargo_root: &str) -> String {
if let (Some(rust_src_root), Some(pos)) = (rust_src_root, dwarf_path.find("/library/")) {
let suffix = &dwarf_path[pos..];
format!("{}/{}", rust_src_root, suffix)
} else if let Some(pos) = dwarf_path
.find(".cargo/registry/")
.or_else(|| dwarf_path.find(".cargo/git/"))
{
let suffix = &dwarf_path[pos + ".cargo/".len()..];
format!("{}/{}", cargo_root, suffix)
} else {
dwarf_path.to_string()
}
}